You can control physical objects by triggering forces. Or maybe you want to respond to collisions, e.g. by substracting health points, or by playing a sound. To specify how the game responds to such physics events, you use Physics Listeners.
The jBullet Physics implementation is stepped at a constant 60 physics ticks per second frame rate. Applying forces or checking for overlaps only has an effect right at a physics update cycle, which is not every frame. If you do physics interactions at arbitrary spots in the simpleUpdate() loop, calls will be dropped at irregular intervals, because they happen out of cycle.
When you write game mechanics that apply forces, you must implement a tick listener (com.jme3.bullet.PhysicsTickListener) for it. The tick listener makes certain the forces are not dropped, but applied in time for the next physics tick.
Also, when you check for overlaps of physical objects with a PhysicsGhostObject, you cannot just go physicsSpace.add(ghost); ghost.getOverLappingObjects()
somewhere. You have to make certain 1 physics tick has passed before the overlapping objects list is filled with data. Again, the PhysicsTickListener does that for you.
When your game mechanics however just poll the current state (e.g. location) of physical objects, or if you only use the Ghost control like a sphere trigger, then you don't need a PhysicsTickListener.
Here's is the declaration of an examplary Physics Control that listens to ticks.
public class MyCustomControl extends RigidBodyControl implements PhysicsTickListener { ... }
When you implement the interface, you have to implement preTick() and postTick() methods.
prePhysicsTick()
is called before the step, here you apply forces (change the state).physicsTick()
is called after the step, here you poll the results (get the current state).@override public void prePhysicsTick(PhysicsSpace space, float f){ // apply state changes ... } @override public void physicsTick(PhysicsSpace space, float f){ // poll game state ... }
If you do not implement the Collision Listener interface (com.jme3.bullet.collision.PhysicsCollisionListener), a collisions will just mean that physical forces are applied automatically. If you just want "Balls rolling, bricks falling" you do not need a listener. If however you want to respond to a collision event (com.jme3.bullet.collision.PhysicsCollisionEvent) with a custom action, then you need to implement the PhysicsCollisionListener interface. Typical actions triggered by collisions include:
Again, here's the example declaration of a Physics Control that uses a collision listener.
public class MyCustomControl extends RigidBodyControl implements PhysicsCollisionListener { ... }
To respond to the PhysicsCollisionEvent you have to override the collision()
method. This gives you access to the event object. Mostly you will be interested in the identity of any two nodes that collided: event.getNodeA()
and event.getNodeB()
.
After you identify the colliding nodes, specify the action to trigger when this pair collides. Note that you cannot know which one will be Node A or Node B, you have to deal with either variant.
public void collision(PhysicsCollisionEvent event) { if ( event.getNodeA().getName().equals("player") ) { final Node node = event.getNodeA(); /** ... do something with the node ... */ } else if ( event.getNodeB().getName().equals("player") ) { final Node node = event.getNodeB(); /** ... do something with the node ... */ } }
The PhysicsCollisionEvent event
gives you access to detailed information about the collision. You already know the event objects can identify which nodes collided, but it even knows how hard they collided:
Method | Purpose |
---|---|
getObjectA() getObjectB() | The two participants in the collision. You cannot know in advance whether some node will be recorded as A or B, you always have to consider both cases. |
getAppliedImpulse() | A float value representing the collision impulse |
getAppliedImpulseLateral1() | A float value representing the lateral collision impulse |
getAppliedImpulseLateral2() | A float value representing the lateral collision impulse |
getCombinedFriction() | A float value representing the collision friction |
getCombinedRestitution() | A float value representing the collision restitution (bounciness) |
Note that after the collision method has been called the object is not valid anymore so you should copy any data you want to keep into local variables.