| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.replica.replicaisland; |
| |
| /** |
| * Component that adds physics to its parent game object. This component implements force |
| * calculation based on mass, impulses, friction, and collisions. |
| */ |
| public class PhysicsComponent extends GameComponent { |
| |
| private float mMass; |
| private float mBounciness; // 1.0 = super bouncy, 0.0 = zero bounce |
| private float mInertia; |
| private float mStaticFrictionCoeffecient; |
| private float mDynamicFrictionCoeffecient; |
| |
| private static final float DEFAULT_MASS = 1.0f; |
| private static final float DEFAULT_BOUNCINESS = 0.1f; |
| private static final float DEFAULT_INERTIA = 0.01f; |
| private static final float DEFAULT_STATIC_FRICTION_COEFFECIENT = 0.05f; |
| private static final float DEFAULT_DYNAMIC_FRICTION_COEFFECIENT = 0.02f; |
| |
| PhysicsComponent() { |
| super(); |
| reset(); |
| setPhase(ComponentPhases.POST_PHYSICS.ordinal()); |
| } |
| |
| @Override |
| public void reset() { |
| // TODO: no reason to call accessors here locally. |
| setMass(DEFAULT_MASS); |
| setBounciness(DEFAULT_BOUNCINESS); |
| setInertia(DEFAULT_INERTIA); |
| setStaticFrictionCoeffecient(DEFAULT_STATIC_FRICTION_COEFFECIENT); |
| setDynamicFrictionCoeffecient(DEFAULT_DYNAMIC_FRICTION_COEFFECIENT); |
| } |
| |
| @Override |
| public void update(float timeDelta, BaseObject parent) { |
| GameObject parentObject = (GameObject) parent; |
| |
| // we look to user data so that other code can provide impulses |
| Vector2 impulseVector = parentObject.getImpulse(); |
| |
| final Vector2 currentVelocity = parentObject.getVelocity(); |
| |
| final Vector2 surfaceNormal = parentObject.getBackgroundCollisionNormal(); |
| if (surfaceNormal.length2() > 0.0f) { |
| resolveCollision(currentVelocity, impulseVector, surfaceNormal, impulseVector); |
| } |
| |
| VectorPool vectorPool = sSystemRegistry.vectorPool; |
| |
| // if our speed is below inertia, we need to overcome inertia before we can move. |
| |
| boolean physicsCausesMovement = true; |
| |
| final float inertiaSquared = getInertia() * getInertia(); |
| |
| Vector2 newVelocity = vectorPool.allocate(currentVelocity); |
| newVelocity.add(impulseVector); |
| |
| if (newVelocity.length2() < inertiaSquared) { |
| physicsCausesMovement = false; |
| } |
| |
| final boolean touchingFloor = parentObject.touchingGround(); |
| |
| GravityComponent gravity = parentObject.findByClass(GravityComponent.class); |
| |
| if (touchingFloor && currentVelocity.y <= 0.0f && Math.abs(newVelocity.x) > 0.0f |
| && gravity != null) { |
| final Vector2 gravityVector = gravity.getGravity(); |
| |
| // if we were moving last frame, we'll use dynamic friction. Else |
| // static. |
| float frictionCoeffecient = Math.abs(currentVelocity.x) > 0.0f ? |
| getDynamicFrictionCoeffecient() : getStaticFrictionCoeffecient(); |
| frictionCoeffecient *= timeDelta; |
| |
| // Friction = cofN, where cof = friction coefficient and N = force |
| // perpendicular to the ground. |
| final float maxFriction = Math.abs(gravityVector.y) * getMass() |
| * frictionCoeffecient; |
| |
| if (maxFriction > Math.abs(newVelocity.x)) { |
| newVelocity.x = (0.0f); |
| } else { |
| newVelocity.x = (newVelocity.x |
| - (maxFriction * Utils.sign(newVelocity.x))); |
| } |
| } |
| |
| if (Math.abs(newVelocity.x) < 0.01f) { |
| newVelocity.x = (0.0f); |
| } |
| |
| if (Math.abs(newVelocity.y) < 0.01f) { |
| newVelocity.y = (0.0f); |
| } |
| |
| // physics-based movements means constant acceleration, always. set the target to the |
| // velocity. |
| if (physicsCausesMovement) { |
| parentObject.setVelocity(newVelocity); |
| parentObject.setTargetVelocity(newVelocity); |
| parentObject.setAcceleration(Vector2.ZERO); |
| parentObject.setImpulse(Vector2.ZERO); |
| } |
| |
| vectorPool.release(newVelocity); |
| } |
| |
| protected void resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal, |
| Vector2 outputImpulse) { |
| VectorPool vectorPool = sSystemRegistry.vectorPool; |
| |
| outputImpulse.set(impulse); |
| |
| Vector2 collisionNormal = vectorPool.allocate(opposingNormal); |
| |
| collisionNormal.normalize(); |
| |
| Vector2 relativeVelocity = vectorPool.allocate(velocity); |
| relativeVelocity.add(impulse); |
| |
| final float dotRelativeAndNormal = relativeVelocity.dot(collisionNormal); |
| |
| // make sure the motion of the entity requires resolution |
| if (dotRelativeAndNormal < 0.0f) { |
| final float coefficientOfRestitution = getBounciness(); // 0 = perfectly inelastic, |
| // 1 = perfectly elastic |
| |
| // calculate an impulse to apply to the entity |
| float j = (-(1 + coefficientOfRestitution) * dotRelativeAndNormal); |
| |
| j /= ((collisionNormal.dot(collisionNormal)) * (1 / getMass())); |
| |
| Vector2 entity1Adjust = vectorPool.allocate(collisionNormal); |
| |
| entity1Adjust.set(collisionNormal); |
| entity1Adjust.multiply(j); |
| entity1Adjust.divide(getMass()); |
| entity1Adjust.add(impulse); |
| outputImpulse.set(entity1Adjust); |
| vectorPool.release(entity1Adjust); |
| |
| } |
| |
| vectorPool.release(collisionNormal); |
| vectorPool.release(relativeVelocity); |
| } |
| |
| protected void resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal, |
| float otherMass, Vector2 otherVelocity, Vector2 otherImpulse, |
| float otherBounciness, Vector2 outputImpulse) { |
| VectorPool vectorPool = sSystemRegistry.vectorPool; |
| |
| Vector2 collisionNormal = vectorPool.allocate(opposingNormal); |
| collisionNormal.normalize(); |
| |
| Vector2 entity1Velocity = vectorPool.allocate(velocity); |
| entity1Velocity.add(impulse); |
| |
| Vector2 entity2Velocity = vectorPool.allocate(otherVelocity); |
| entity2Velocity.add(otherImpulse); |
| |
| Vector2 relativeVelocity = vectorPool.allocate(entity1Velocity); |
| relativeVelocity.subtract(entity2Velocity); |
| |
| final float dotRelativeAndNormal = relativeVelocity.dot(collisionNormal); |
| |
| // make sure the entities' motion requires resolution |
| if (dotRelativeAndNormal < 0.0f) { |
| final float bounciness = Math.min(getBounciness() + otherBounciness, 1.0f); |
| final float coefficientOfRestitution = bounciness; // 0 = perfectly inelastic, |
| // 1 = perfectly elastic |
| |
| // calculate an impulse to apply to both entities |
| float j = (-(1 + coefficientOfRestitution) * dotRelativeAndNormal); |
| |
| j /= ((collisionNormal.dot(collisionNormal)) * (1 / getMass() + 1 / otherMass)); |
| |
| Vector2 entity1Adjust = vectorPool.allocate(collisionNormal); |
| entity1Adjust.multiply(j); |
| entity1Adjust.divide(getMass()); |
| entity1Adjust.add(impulse); |
| |
| outputImpulse.set(entity1Adjust); |
| |
| // TODO: Deal impulses both ways. |
| /* |
| * Vector3 entity2Adjust = (collisionNormal j); |
| * entity2Adjust[0] /= otherMass; |
| * entity2Adjust[1] /= otherMass; |
| * entity2Adjust[2] /= otherMass; |
| * |
| * const Vector3 newEntity2Impulse = otherImpulse + entity2Adjust; |
| */ |
| |
| vectorPool.release(entity1Adjust); |
| } |
| |
| vectorPool.release(collisionNormal); |
| vectorPool.release(entity1Velocity); |
| vectorPool.release(entity2Velocity); |
| vectorPool.release(relativeVelocity); |
| } |
| |
| public float getMass() { |
| return mMass; |
| } |
| |
| public void setMass(float mass) { |
| mMass = mass; |
| } |
| |
| public float getBounciness() { |
| return mBounciness; |
| } |
| |
| public void setBounciness(float bounciness) { |
| mBounciness = bounciness; |
| } |
| |
| public float getInertia() { |
| return mInertia; |
| } |
| |
| public void setInertia(float inertia) { |
| mInertia = inertia; |
| } |
| |
| public float getStaticFrictionCoeffecient() { |
| return mStaticFrictionCoeffecient; |
| } |
| |
| public void setStaticFrictionCoeffecient(float staticFrictionCoeffecient) { |
| mStaticFrictionCoeffecient = staticFrictionCoeffecient; |
| } |
| |
| public float getDynamicFrictionCoeffecient() { |
| return mDynamicFrictionCoeffecient; |
| } |
| |
| public void setDynamicFrictionCoeffecient(float dynamicFrictionCoeffecient) { |
| mDynamicFrictionCoeffecient = dynamicFrictionCoeffecient; |
| } |
| |
| } |