Physics event handling

During the course of a physics simulation, colliders will collides, and sensors will be activated. These events can be retrieved in order to apply custom game logics. There are currently two types of events:

  • Contact events triggered when two colliders start or stop having contact points.
  • Proximity events triggered when one sensor collider and another collider start of step touching.

In order to listen to these physics events, we need to:

  1. Create and EventQueue which is responsible for collecting events occurring during one timestep.
  2. Call world.step(eventQueue) instead of world.step() to advance the simulation.
  3. Iterate through the events using eventQueue.drainContactEvents and eventsQueue.drainProximityEvents.
import('@dimforge/rapier2d').then(RAPIER => {
let gravity = new RAPIER.Vector2(0.0, -9.81);
let world = new RAPIER.World(gravity);
let eventQueue = new RAPIER.EventQueue(true);
// Game loop. Replace by your own game loop system.
let gameLoop = () => {
world.step(eventQueue);
eventQueue.drainContactEvents((handle1, handle2, contactStarted) => {
console.log("Contact between:", handle1, "and", handle2, ". Started:", contactStarted);
});
eventQueue.drainProximityEvents((handle1, handle2, prevProximity, newProximity) => {
console.log("Proximity between: ", handle1, "and", handle2, ". Previous proximity:", prevProximity, "new proximity: ", newProximity);
});
setTimeout(gameLoop);
};
gameLoop();
})

The parameter of the EventQueue constructor indicates whether or not the queue should be automatically cleared at the beginning of each step call. Setting this to false will allow us to handle events that were generated by previous timesteps. However, this may cause a memory overflow if the queue is never cleared or drained. If it is set to true, then we need to handle the all physics events in-between each calls to step; all unhandled events that occurred before the last call to step will be lost.

Handling the events is achieved by calling the corresponding drain function. This function will clear the event queue and pass all its items to a JS closure:

  • The eventQueue.drainContactEvents drains all the contact events. The input closure is expected to take three arguments: two integers and one boolean. The twe integers are the handles of the colliders involved in the contact. We can retrieve the actual collider reference form its integer handle using world.getCollider(handle). The boolean indicates whether the two colliders are starting to be in contact (true) or no longer have any contact point (false).
  • The eventQueue.drainProximityEvents drain all the proximity events. The input closure is expected to take four arguments, two integers and two integer enumerations of type RAPIER.Proximity. The two integers are the handles of the colliders involved in the proximity. Because this is a proximity event and not a contact, one of these colliders (or both) will be a sensor. The first proximity enum represents the previous known proximity state, and the the second proximity enum represents the new proximity state. For example if prevProximity == RAPIER.Proximity.Disjoint and newProximity == RAPIER.Proximity.Intersecting then the two colliders were disjoint during the last timestep and are now intersecting.