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 PhysicsComponent()37 PhysicsComponent() { 38 super(); 39 reset(); 40 setPhase(ComponentPhases.POST_PHYSICS.ordinal()); 41 } 42 43 @Override reset()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 update(float timeDelta, BaseObject parent)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 resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal, Vector2 outputImpulse)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 resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal, float otherMass, Vector2 otherVelocity, Vector2 otherImpulse, float otherBounciness, Vector2 outputImpulse)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 getMass()225 public float getMass() { 226 return mMass; 227 } 228 setMass(float mass)229 public void setMass(float mass) { 230 mMass = mass; 231 } 232 getBounciness()233 public float getBounciness() { 234 return mBounciness; 235 } 236 setBounciness(float bounciness)237 public void setBounciness(float bounciness) { 238 mBounciness = bounciness; 239 } 240 getInertia()241 public float getInertia() { 242 return mInertia; 243 } 244 setInertia(float inertia)245 public void setInertia(float inertia) { 246 mInertia = inertia; 247 } 248 getStaticFrictionCoeffecient()249 public float getStaticFrictionCoeffecient() { 250 return mStaticFrictionCoeffecient; 251 } 252 setStaticFrictionCoeffecient(float staticFrictionCoeffecient)253 public void setStaticFrictionCoeffecient(float staticFrictionCoeffecient) { 254 mStaticFrictionCoeffecient = staticFrictionCoeffecient; 255 } 256 getDynamicFrictionCoeffecient()257 public float getDynamicFrictionCoeffecient() { 258 return mDynamicFrictionCoeffecient; 259 } 260 setDynamicFrictionCoeffecient(float dynamicFrictionCoeffecient)261 public void setDynamicFrictionCoeffecient(float dynamicFrictionCoeffecient) { 262 mDynamicFrictionCoeffecient = dynamicFrictionCoeffecient; 263 } 264 265 } 266