Physics Listeners

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.

Physics Tick Listener

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 (Not) to Use Tick Listener?

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.

How to Listen to Physics Ticks

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.

@override
public void prePhysicsTick(PhysicsSpace space, float f){
  // apply state changes ...
}
@override
public void physicsTick(PhysicsSpace space, float f){
  // poll game state ...
}

Physics Collision Listener

When (Not) to Use Collision Listener

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:

How to Listen to Collisions

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 ... */
        }
    }

Note that after the collision() method ends, the PhysicsCollisionEvent is cleared. You must get all objects and values you need within the collision() method.

Reading Details From a PhysicsCollisionEvent

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:

MethodPurpose
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.

view online version