Event handling and sensors

Besides the physics simulation itself, applications often need to perform specific actions when some particular events occur inside of the physics world. For example, we might want to generate sounds when two solids collide, or open a door when a player comes close to it, detect when a projectile hits a target, etc. To allow those kinds of actions, it is possible to know when two objects start/stop colliding.

In addition, a special kind of collider, called sensors that do not generate any contacts points, but will detect when they start/stop touching another collider (be it another sensor or not). This is often useful to detect, e.g., that a player entered a specific area.

Finally it is possible to retrieve at any time the set of colliders interacting with another collider using various iterators provided by the contact and intersection graphs.


Sensors are a special type of collider that don't generate any contact. Like regular colliders, they have to be added to the ColliderSet, are given a geometric shape, and are attached to a rigid-body. They are commonly used for detecting intersection, e.g., to detect when a player enters a specific area or is close to a door.

The creation of a sensor is identical to the creation of a regular collider except that its sensor flag is set to true:

let sensor = ColliderBuilder::ball(0.5)
// Add to the collider set.
let sensor_handle = collider_set.insert(sensor, parent_body, &mut body_set);

As presented in the next section sensors and regular colliders both generate events of different types to distinguish events due to contacts from events due to sensors.

Interaction handling#

I order to handle physics events, it is necessary to collect them using a structure implementing the EventHandler trait. Because Rapier can be paralellized, this event handler must be Send + Sync. One such structure provided by Rapier is the pipeline::ChannelEventCollector. This event collector contains channels from the crossbeam crate. These channels will be populated with events during each called to pipeline.step:

use rapier2d::na::Vector2;
use rapier2d::dynamics::{JointSet, RigidBodySet, IntegrationParameters};
use rapier2d::geometry::{BroadPhase, NarrowPhase, ColliderSet};
use rapier2d::pipeline::{PhysicsPipeline, ChannelEventCollector};
fn main() {
// Here the gravity is -9.81 along the y axis.
let mut pipeline = PhysicsPipeline::new();
let gravity = Vector2::new(0.0, -9.81);
let integration_parameters = IntegrationParameters::default();
let mut broad_phase = BroadPhase::new();
let mut narrow_phase = NarrowPhase::new();
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let mut joints = JointSet::new();
// Initialize the event collector.
let (contact_send, contact_recv) = crossbeam::channel::unbounded();
let (intersection_send, intersection_recv) = crossbeam::channel::unbounded();
let event_handler = ChannelEventCollector::new(intersection_send, contact_send);
let physics_hooks = (); // Ignore physics hooks.
// Run the simulation in the game loop.
loop {
&mut broad_phase,
&mut narrow_phase,
&mut bodies,
&mut colliders,
&mut joints,
while let Ok(intersection_event) = intersection_recv.try_recv() {
// Handle the intersection event.
while let Ok(contact_event) = contact_recv.try_recv() {
// Handle the contact event.

Contact and intersection events identify the involved colliders by their handle. It is possible to retrieve the handle of the body a collider is attached to: colliders.get(collider_handle).unwrap().parent().

Intersection events#

Intersection events are triggered when two sensors, or one collider and one sensor, transition between intersecting or not.

One intersection event has the type ProximityEvent:

collider1The handle of the first collider/sensor involved in the intersection.
collider2The handle of the second collider/sensor involved in the intersection.
intersectingSet to true if the colliders are intersecting.

Contact events#

Contact events are triggered when two colliders transition between having zero contact points and at least one. Transitioning between one to more than one contact points is not reported.

An iterator through all contact events may be retrieved by the .contact_events() method of the World. The yielded ContactEvent enum has two variants:

  1. ContactEvent::Started(h1, h2) to indicate the transition between 0 to at least one contact point.
  2. ContactEvent::Stopped(h1, h2) to indicate the transition between at least one contact point to 0.

Here h1 and h2 identify the handles of the colliders involved in the contact.