blob: 958283a6bc59941b6fa61190db10a692324d99c6 [file] [log] [blame]
/*
* 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;
}
}