1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.replica.replicaisland; 18 19 /** 20 * Component that adds physics to its parent game object. This component implements force 21 * calculation based on mass, impulses, friction, and collisions. 22 */ 23 public class PhysicsComponent extends GameComponent { 24 25 private float mMass; 26 private float mBounciness; // 1.0 = super bouncy, 0.0 = zero bounce 27 private float mInertia; 28 private float mStaticFrictionCoeffecient; 29 private float mDynamicFrictionCoeffecient; 30 31 private static final float DEFAULT_MASS = 1.0f; 32 private static final float DEFAULT_BOUNCINESS = 0.1f; 33 private static final float DEFAULT_INERTIA = 0.01f; 34 private static final float DEFAULT_STATIC_FRICTION_COEFFECIENT = 0.05f; 35 private static final float DEFAULT_DYNAMIC_FRICTION_COEFFECIENT = 0.02f; 36 37 PhysicsComponent() { 38 super(); 39 reset(); 40 setPhase(ComponentPhases.POST_PHYSICS.ordinal()); 41 } 42 43 @Override 44 public void reset() { 45 // TODO: no reason to call accessors here locally. 46 setMass(DEFAULT_MASS); 47 setBounciness(DEFAULT_BOUNCINESS); 48 setInertia(DEFAULT_INERTIA); 49 setStaticFrictionCoeffecient(DEFAULT_STATIC_FRICTION_COEFFECIENT); 50 setDynamicFrictionCoeffecient(DEFAULT_DYNAMIC_FRICTION_COEFFECIENT); 51 } 52 53 @Override 54 public void update(float timeDelta, BaseObject parent) { 55 GameObject parentObject = (GameObject) parent; 56 57 // we look to user data so that other code can provide impulses 58 Vector2 impulseVector = parentObject.getImpulse(); 59 60 final Vector2 currentVelocity = parentObject.getVelocity(); 61 62 final Vector2 surfaceNormal = parentObject.getBackgroundCollisionNormal(); 63 if (surfaceNormal.length2() > 0.0f) { 64 resolveCollision(currentVelocity, impulseVector, surfaceNormal, impulseVector); 65 } 66 67 VectorPool vectorPool = sSystemRegistry.vectorPool; 68 69 // if our speed is below inertia, we need to overcome inertia before we can move. 70 71 boolean physicsCausesMovement = true; 72 73 final float inertiaSquared = getInertia() * getInertia(); 74 75 Vector2 newVelocity = vectorPool.allocate(currentVelocity); 76 newVelocity.add(impulseVector); 77 78 if (newVelocity.length2() < inertiaSquared) { 79 physicsCausesMovement = false; 80 } 81 82 final boolean touchingFloor = parentObject.touchingGround(); 83 84 GravityComponent gravity = parentObject.findByClass(GravityComponent.class); 85 86 if (touchingFloor && currentVelocity.y <= 0.0f && Math.abs(newVelocity.x) > 0.0f 87 && gravity != null) { 88 final Vector2 gravityVector = gravity.getGravity(); 89 90 // if we were moving last frame, we'll use dynamic friction. Else 91 // static. 92 float frictionCoeffecient = Math.abs(currentVelocity.x) > 0.0f ? 93 getDynamicFrictionCoeffecient() : getStaticFrictionCoeffecient(); 94 frictionCoeffecient *= timeDelta; 95 96 // Friction = cofN, where cof = friction coefficient and N = force 97 // perpendicular to the ground. 98 final float maxFriction = Math.abs(gravityVector.y) * getMass() 99 * frictionCoeffecient; 100 101 if (maxFriction > Math.abs(newVelocity.x)) { 102 newVelocity.x = (0.0f); 103 } else { 104 newVelocity.x = (newVelocity.x 105 - (maxFriction * Utils.sign(newVelocity.x))); 106 } 107 } 108 109 if (Math.abs(newVelocity.x) < 0.01f) { 110 newVelocity.x = (0.0f); 111 } 112 113 if (Math.abs(newVelocity.y) < 0.01f) { 114 newVelocity.y = (0.0f); 115 } 116 117 // physics-based movements means constant acceleration, always. set the target to the 118 // velocity. 119 if (physicsCausesMovement) { 120 parentObject.setVelocity(newVelocity); 121 parentObject.setTargetVelocity(newVelocity); 122 parentObject.setAcceleration(Vector2.ZERO); 123 parentObject.setImpulse(Vector2.ZERO); 124 } 125 126 vectorPool.release(newVelocity); 127 } 128 129 protected void resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal, 130 Vector2 outputImpulse) { 131 VectorPool vectorPool = sSystemRegistry.vectorPool; 132 133 outputImpulse.set(impulse); 134 135 Vector2 collisionNormal = vectorPool.allocate(opposingNormal); 136 137 collisionNormal.normalize(); 138 139 Vector2 relativeVelocity = vectorPool.allocate(velocity); 140 relativeVelocity.add(impulse); 141 142 final float dotRelativeAndNormal = relativeVelocity.dot(collisionNormal); 143 144 // make sure the motion of the entity requires resolution 145 if (dotRelativeAndNormal < 0.0f) { 146 final float coefficientOfRestitution = getBounciness(); // 0 = perfectly inelastic, 147 // 1 = perfectly elastic 148 149 // calculate an impulse to apply to the entity 150 float j = (-(1 + coefficientOfRestitution) * dotRelativeAndNormal); 151 152 j /= ((collisionNormal.dot(collisionNormal)) * (1 / getMass())); 153 154 Vector2 entity1Adjust = vectorPool.allocate(collisionNormal); 155 156 entity1Adjust.set(collisionNormal); 157 entity1Adjust.multiply(j); 158 entity1Adjust.divide(getMass()); 159 entity1Adjust.add(impulse); 160 outputImpulse.set(entity1Adjust); 161 vectorPool.release(entity1Adjust); 162 163 } 164 165 vectorPool.release(collisionNormal); 166 vectorPool.release(relativeVelocity); 167 } 168 169 protected void resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal, 170 float otherMass, Vector2 otherVelocity, Vector2 otherImpulse, 171 float otherBounciness, Vector2 outputImpulse) { 172 VectorPool vectorPool = sSystemRegistry.vectorPool; 173 174 Vector2 collisionNormal = vectorPool.allocate(opposingNormal); 175 collisionNormal.normalize(); 176 177 Vector2 entity1Velocity = vectorPool.allocate(velocity); 178 entity1Velocity.add(impulse); 179 180 Vector2 entity2Velocity = vectorPool.allocate(otherVelocity); 181 entity2Velocity.add(otherImpulse); 182 183 Vector2 relativeVelocity = vectorPool.allocate(entity1Velocity); 184 relativeVelocity.subtract(entity2Velocity); 185 186 final float dotRelativeAndNormal = relativeVelocity.dot(collisionNormal); 187 188 // make sure the entities' motion requires resolution 189 if (dotRelativeAndNormal < 0.0f) { 190 final float bounciness = Math.min(getBounciness() + otherBounciness, 1.0f); 191 final float coefficientOfRestitution = bounciness; // 0 = perfectly inelastic, 192 // 1 = perfectly elastic 193 194 // calculate an impulse to apply to both entities 195 float j = (-(1 + coefficientOfRestitution) * dotRelativeAndNormal); 196 197 j /= ((collisionNormal.dot(collisionNormal)) * (1 / getMass() + 1 / otherMass)); 198 199 Vector2 entity1Adjust = vectorPool.allocate(collisionNormal); 200 entity1Adjust.multiply(j); 201 entity1Adjust.divide(getMass()); 202 entity1Adjust.add(impulse); 203 204 outputImpulse.set(entity1Adjust); 205 206 // TODO: Deal impulses both ways. 207 /* 208 * Vector3 entity2Adjust = (collisionNormal j); 209 * entity2Adjust[0] /= otherMass; 210 * entity2Adjust[1] /= otherMass; 211 * entity2Adjust[2] /= otherMass; 212 * 213 * const Vector3 newEntity2Impulse = otherImpulse + entity2Adjust; 214 */ 215 216 vectorPool.release(entity1Adjust); 217 } 218 219 vectorPool.release(collisionNormal); 220 vectorPool.release(entity1Velocity); 221 vectorPool.release(entity2Velocity); 222 vectorPool.release(relativeVelocity); 223 } 224 225 public float getMass() { 226 return mMass; 227 } 228 229 public void setMass(float mass) { 230 mMass = mass; 231 } 232 233 public float getBounciness() { 234 return mBounciness; 235 } 236 237 public void setBounciness(float bounciness) { 238 mBounciness = bounciness; 239 } 240 241 public float getInertia() { 242 return mInertia; 243 } 244 245 public void setInertia(float inertia) { 246 mInertia = inertia; 247 } 248 249 public float getStaticFrictionCoeffecient() { 250 return mStaticFrictionCoeffecient; 251 } 252 253 public void setStaticFrictionCoeffecient(float staticFrictionCoeffecient) { 254 mStaticFrictionCoeffecient = staticFrictionCoeffecient; 255 } 256 257 public float getDynamicFrictionCoeffecient() { 258 return mDynamicFrictionCoeffecient; 259 } 260 261 public void setDynamicFrictionCoeffecient(float dynamicFrictionCoeffecient) { 262 mDynamicFrictionCoeffecient = dynamicFrictionCoeffecient; 263 } 264 265 } 266