Collision-detection is a two-steps process. First the
BroadPhase detects pairs of colliders
that are potentially in contact or intersecting. Second, the
NarrowPhase processes all these pairs in order
to compute contacts points and generate contact or intersection events.
All the pairs detected by the broad-phase are stored into two graph structures:
- The contact graph stores all the potential contact pairs (between two non-sensor colliders) as well as the contact points generated by the narrow-phase.
- The intersection graph stores all the potential intersection pairs (between a sensor collider and another collider) as well as the result of the boolean intersection test executed by the narrow-phase.
These two graphs are part of the
NarrowPhase structure and are automatically updated by the
CollisionPipeline. Each node of these graph contains a collide handle
and there is one graph edge per pair detected by the broad-phase.
These events can be read using Bevy's event system with
Contact and intersection events identify the involved colliders by their handle. It is possible to retrieve the
corresponding entity by calling
Intersection events are triggered when two colliders (where at least one of them is a sensor) transition between
intersecting or not. One intersection event has the type
|The handle of the first collider involved in the intersection.|
|The handle of the second collider involved in the intersection.|
|Set to |
Intersection events are only generated between two colliders (where at least one of the is a sensor) if at
least one of them has the
ActiveEvents::INTERSECTION_EVENTS flag in its active events.
Contact events are triggered when two non-sensor colliders transition between having zero contact points and having
at least one contact point. Transitioning between having one contact point to having more than one contact points is
not reported an event. The
ContactEvent enum has two variants:
ContactEvent::Started(h1, h2)to indicate the transition between 0 to at least one contact point.
ContactEvent::Stopped(h1, h2)to indicate the transition between at least one contact point to 0.
h2 identify the handles of the colliders involved in the contact. The
also provides the contact pair.
Contact events are only generated between two non-sensor colliders if at least one of them has
ActiveEvents::CONTACT_EVENTS flag in its active events.
The contact graph can be read in order to determine whether two specific non-sensor colliders are in contact, or to determine all the non-sensor colliders in contact with one particular non-sensor collider. Contact points and contact normals will also be provided when a contact exists.
The contact geometry (contact points, contact normal, penetration depth, etc.) can be read from the contact manifolds stored in a contact pair:
- Each contact pair may contain multiple contact manifolds. Each contact manifold represents a set of contacts sharing the same contact normal.
- Each contact manifold contains the list of geometric contacts detected by the narrow-phase.
- Each contact manifold also contains a list of contacts that were processed by the constraints solver for force calculation (aka. the solver contacts). These solver contacts are a subset of the contacts detected by the narrow-phase, expressed in a way that is more efficient for the constraints solver to process. These solver contacts can be modified or deleted by the user using contact modification.
All the geometric contact data are expressed in the local-space of the colliders. The solver contacts are expressed in world-space.
Because the solver contacts can be modified by the user and are expressed in world-space, they are transients by nature:
they are recomputed at each frame from the geometric contacts. Because of their transient nature, the constraint solver will
store the forces it computes inside of the geometric contacts
TrackedContact::data::impulse field instead of the solver
Keep in mind that the contact graph contains one graph edge per pair detected by the broad-phase. So the fact that a contact pair can be found in the graph doesn't mean that the corresponding colliders are actually in contact (they may just be very close to one another, without touching). It is necessary to check either:
ContactPair::has_any_active_contactif you need to know if there exist at least one solver contact between the colliders.
- the length of
ContactManifold:pointsfor each manifold in
ContactPair::manifoldsto determine if the colliders are really geometrically touching (independently from contact-modification).
There will always be only up to one contact manifold between two colliders with convex primitive shapes. If one collider has a shape composed of several pieces (trimesh, polyline, heightfield, or compound shape) then there will be multiple contact manifolds, one for each piece that may result in an actual contact.
Finally, keep in mind that the contacts and contact manifolds field names frequently end with a digit
contact_pair.manifolds.local_n2. Fields ending with
1 relate to the collider identified by
contact_pair.collider1. Fields ending with the digit
relate to the collider identified by
In other words
local_n1 is the contact normal expressed in the local space of the collider
it points towards the exterior of the shape of
collider_pair.collider1. On the other hand,
local_n2 is expressed in
the local space of the collider
collider_pair.collider2 and points towards the exterior of the shape of
The contact pair returned by
narrow_phase.contact_pair(handle1, handle2) does not necessarily
contact_pair.collider1 == handle1 && contact_pair.collider2 == handle2. It could be swapped:
contact_pair.collider1 == handle2 && contact_pair.collider2 == handle1.
So keep that in mind when reading the contact information because it's
that determine to what collider the digits
2 relate in the contacts and contact manifolds fields.
The intersection graph can be read in order to determine whether two specific colliders (assuming at least one of them is a sensor) are intersecting, or to determine all the colliders intersecting one particular collider (assuming at least one collider of each pair is a sensor). The intersection graph contains one graph edge for each pair of colliders such that:
- At least one of the collider is a sensor.
- And they are close enough so the broad-phase considers they have a chance to be intersecting.
Each such edge contains one boolean indicating if the colliders are actually intersecting or not:
Keep in mind that intersection tests are performed between two colliders only if at least one of the colliders is a sensor. If they are both non-sensor colliders then they will be involved in the contact graph instead of the intersection graph.
Physics hooks are user-defined callbacks used to change the behavior of the physics simulation. In particular, they can be used to filter contacts (in a more flexible way than collision groups and solver groups) and to modify contacts before they are processed by the constraints solver.
Physics hooks are given as a resource of type
UserData is an arbitrary
component type that you would like to access inside of the hook methods (useful for example to access custom data
attached to the entities to guide contact filtering). For physics hooks to work, the following steps must be taken:
RapierPhysicsPluginmust be parametrized by the same
UserDataas your physics hooks, e.g.,
- The custom physics hooks must implement the
- The hooks must be inserted as a resource:
If you don't need any user-data for your physics hooks, you can use
NoUserData as the user data:
Sometimes, collision groups and solver groups are not flexible enough to achieve the desired behavior. In that case, the contact filtering hooks let you apply custom rules to filter contact pairs and intersection pairs:
- For each potential contact pair (between two non-sensor colliders) detected by the broad-phase, if at least one
of the colliders involved in the pair has the bit
ActiveHooks::FILTER_CONTACT_PAIRSenabled in its active hooks, then
PhysicsHooks::filter_contact_pairwill be called. If this filter returns
Nonethen no contact computation will happen for this pair of colliders. If it returns
Somethen the narrow-phase will compute contact points.
- For each potential intersection pair (between a sensor colliders and another collider) detected by the broad-phase, if
at least one of the colliders involved in the pair has the bit
ActiveHooks::FILTER_INTERSECTION_PAIRSenabled in its active hooks, then
PhysicsHooks::filter_intersection_pairwill be called. If this filter returns
falsethen no intersection computation will happen for this pair of colliders. If it returns
truethen the narrow-phase will test whether or not they are intersecting.
Some(flags) it needs to provide a set of solver flags for this contact
pair. These solver flags indicate what happen with the contacts of this contact pair afterwards:
- If the returned
SolverFlags::COMPUTE_IMPULSESbit, then the constraints solver will compute forces for these contacts. If this bit is not included in the returned flags, then no contact force will be computed for this pair of colliders.
Right now there is no solver flags other than
SolverFlags::COMPUTE_IMPULSES. Other flags may be added in the future.
It is possible to modify contacts after they have been computed by the narrow-phase. Contact-modification can have multiple advanced usages, for example:
- The simulation of conveyor belts by modifying the
tangent_velocityof solver contacts.
- The simulation of one-way-platforms by deleting some contacts depending on the contact normal.
- The simulation of colliders with non-uniform friction or non-uniform restitution coefficients, i.e., friction or restitution coefficients that depend on the contact points location.
PhysicsHooks::modify_solver_contacts methods is called on each contact manifold between two colliders where at
least one of them has the
ActiveHooks::MODIFY_SOLVER_CONTACTS flag enabled in its
Contact modification can be used to remove some (or all) solver contacts from a contact manifold. However, it cannot be used to add new contacts manually. If this is something that could useful to you, please consider openning an issue to let us know about your use-case so we can see if this is worth adding.
Contact-modification lets you change most characteristics of a contact, including the contact normal, contact friction/restitution
coefficients, contact penetration depth, and warmstart impulses. None of these modifications are persistent (they
are overwritten during the next timestep). There is one exception though: you can modify a
user_data associated to
user_data will persist throughout timesteps as long as the
ContactManifold remains alive
(i.e. as long as some contacts exist between the touching parts of the colliders shapes). This can be useful
to apply modification rules that depend on previous states of the contact (like whether or not this contact manifold
existed during previous timesteps).
Continuous Collision Detection (CCD) is used to make sure that fast-moving objects don't miss any contacts (a problem usually called tunneling). See the rigid-body CCD section for details.