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 by polling physics events. In addition, a special kind of colliders, called sensors allows the addition of geometric shapes (that may be attached optionally to a body part) to the world that do not generate any collisions, but will still 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.

Sensors§

Sensors are a special type of colliders that don’t generate any contact. Like colliders, the have to be added to the world, are given a geometric shape, and are attached to a body part (or the BodyHandle::ground()). They are commonly used for detecting proximity, e.g., to detect when a player enters a specific area or is close to a door. The sensor demo shows a ball-shaped sensor attached to a cube. All colliders (except the ground) that intersect this ball are coloured in yellow.

The creation of a sensor is almost identical to the creation of a regular collider:

let sensor_handle = world.add_sensor(
    shape,      // The geometric shape of the sensor.
    parent,     // The handle of the body part this sensor is attached to.
    position,   // The relative position of this sensor wrt. its parent.
);

The description of each argument is the same as for colliders, except that the margin and material arguments are not applicable to sensors since they don’t generate any contact. Since sensors are just special kinds of colliders, they can be removed from the world using world.remove_colliders(...) and retrieved with world.collider(...). As presented in the next section sensors and colliders both generate events of different types to distinguish events due to contacts from events due to sensors.

Note

Because sensors are just special cases of colliders, they can be given a specific collision group so they generate events only with a subset of sensors and colliders. The procedure for setting up collision groups is the same as for regular colliders as described in that section.

Event handling§

Events generated by nphysics actually come from the ncollide2d or the ncollide3d crate which is responsible for all the collision detection. Therefore, the Event Handling section on ncollide’s user guide is directly applicable here. We distinguish two kinds of events: proximity events generated by sensors and contact events generated when two non-sensor colliders touch. Events are generated by the physics world at each timestep. It is possible to iterate through all the events generated by the last call to world.step():

for proximity in world.proximity_events() {
    // Handle proximity events.
}

for contact in world.contact_events() {
    // Handle contact events.
}

Warning

Iterating through all events will not clear the internal event pool. Therefore, it is possible to write several loops like above, each of which will yield the same events in the same order. However, keep in mind all events that occurred before the last call to world.step() are no longer retrievable.

Note

Contact and proximity events identify the involved colliders by their handle. It is possible to retrieve the handle of the body part a collider is attached to with world.collider_body_handle(collider_handle). This will return BodyHandle::ground() if the collider is attached to the ground.

Proximity events§

Proximity events are triggered when two sensors, or one collider and one sensor, transition between two different proximity statuses. There are three possible proximity statuses:

  1. Proximity::Intersecting indicates two sensors (or one sensor and one collider) are touching/penetrating.
  2. Proximity::WithinMargin indicates two sensors (or one sensor and one collider) are not touching but separated by a distance smaller than world.prediction().
  3. Proximity::Disjoint indicates two sensors (or one sensor and one collider) are no longer touching, and are too far apart to be within the world.prediction().

An iterator through all proximity events may be retrieved using the .proximity_events() method of the World. This yields ProximityEvent structures:

Field Description
collider1 The handle of the first collider/sensor involved in the proximity.
collider2 The handle of the second collider/sensor involved in the proximity.
prev_status The previous proximity status of the colliders/sensors.
new_status The current proximity status of the colliders/sensors.

Only transitions are reported, so prev_status != new_status is guaranteed. On the other hand, keep in mind that the body parts a collider/sensor is attached to is not required to have a smooth motion. Thus, the transition from, e.g., Proximity::Intersecting to Proximity::Disjoint is possible even if a smooth motion would have necessarily triggered transitions from ::Intersecting to ::WithinMargin and then from ::WithMargin to ::Disjoint instead.

Contact events§

Contact events are triggered when two colliders transition between having zero contact point 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(handle1, handle2) to indicate the transition between 0 to at least one contact point.
  2. ContactEvent::Stopped(handle1, handle2) to indicate the transition between at least one contact point to 0.

In case of starting contacts, it is possible to retrieve the contacts by obtaining a reference to the contact algorithm with:

let collision_world = world.collision_world();
let contact_pair = collision_world.contact_pair(handle1, handle2);

The resulting contact algorithm, i.e., a ContactManifoldGenerator trait-object, allows the retrieval of the computed contact manifolds. Refer to the ContactManifoldGenerator trait for details.


Contact models Performance tuning