Home | History | Annotate | Download | only in replicaisland
      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