# Colliders

Colliders represent the geometric shapes that generate contacts and collision events when they touch. Attaching one or multiple colliders to a rigid body allow the rigid-body to be affected by contact forces.

## Creation and insertion

A collider is created by adding the `Collider`

component. Other components like `Transform`

,
`Sensor`

, `Friction`

, etc. can be added to customize the collider.

The following example shows several initializations of components to customize collider being built. The input values are just random so using this example as-is will not lead to a useful result.

- Example 2D
- Example 3D

`use bevy_rapier2d::prelude::*;`

commands

.spawn(Collider::cuboid(1.0, 2.0))

.insert(Sensor)

.insert(TransformBundle::from(Transform::from_xyz(2.0, 0.0, 0.0)))

.insert(Friction::coefficient(0.7))

.insert(Restitution::coefficient(0.3))

.insert(ColliderMassProperties::Density(2.0));

`use bevy_rapier3d::prelude::*;`

commands

.spawn(Collider::cuboid(1.0, 2.0, 1.0))

.insert(Sensor)

.insert(TransformBundle::from(Transform::from_xyz(2.0, 0.0, 0.0)))

.insert(Friction::coefficient(0.7))

.insert(Restitution::coefficient(0.3))

.insert(ColliderMassProperties::Density(2.0));

A collider can optionally be attached to a rigid-body. Attaching a collider to a rigid-body will result in the rigid-body being affected by collisions. The collider's position will be automatically updated from the position of the rigid-body it is attached to. There are two ways of attaching a collider to a rigid-body. The second way allows you to attach multiple colliders to the same rigid-body:

- Attach the
`Collider`

to the same entity as the`RigidBody`

. - Attach the
`Collider`

to an entity that is a child of the entity containing the`RigidBody`

.

`// Attach a single collider to a rigid-body.`

commands

.spawn(RigidBody::Dynamic)

.insert(Collider::ball(0.5));

// Attach a multiple colliders to a rigid-body.

commands

.spawn((RigidBody::Dynamic, GlobalTransform::default()))

.with_children(|children| {

children

.spawn(Collider::ball(0.5))

// Position the collider relative to the rigid-body.

.insert(TransformBundle::from(Transform::from_xyz(0.0, 0.0, -1.0)));

children

.spawn(Collider::ball(0.5))

// Position the collider relative to the rigid-body.

.insert(TransformBundle::from(Transform::from_xyz(0.0, 0.0, 1.0)));

});

## Collider type

There are two types of colliders:

- A
**solid collider**represents a geometric shape that can have contact points with other colliders to generate contact forces to prevent objects from penetrating-each-others. **Sensor colliders**on the other end don't generate contacts: they only generate intersection events when one sensor collider and another collider start/stop touching. Sensor colliders are generally used to detect when something enters an area. Note that, for symmetry with non-sensor colliders, sensors*do*contribute to the mass of a rigid-body they are attached to.

By default a collider is a solid collider. This can be changed to a sensor when constructing the collider, or after its construction:

`/* Set the collider sensor when the collider is created. */`

commands.spawn(Collider::ball(0.5)).insert(Sensor);

`/* Change the collider sensor status inside of a system. */`

fn modify_collider_type(mut commands: Commands, sensors: Query<Entity, With<Sensor>>) {

for entity in sensors.iter() {

commands.entity(entity).remove::<Sensor>();

}

}

## Shapes

### Overview

The main characteristic of a collider is its geometric shape. The supported shapes are illustrated bellow:

Shapes only hold information about their geometry. Their world-space position is given by the collider's position. Balls, cuboids, capsules, cylinders, and cones are all described by their half-height and/or radius. Compound shapes, convex meshes, triangle meshes, heightfields, and polylines are more complicated shapes described in the next paragraphs.

### Convex meshes

A convex mesh is a shape such that, if two points are part of the shape, then the segment between these two points is also part of the shape:

There are two ways of creating a collider with a convex shape:

- Using
`Collider::convex_hull(points)`

. This is the simplest approach: it will automatically compute the convex hull of the given set of points. A**convex hull**is the smallest convex shape that contains all the given points. - Using
`Collider::convex_mesh(points, indices)`

in 3D or`Collider::convex_polyline(points)`

in 2D. This takes a mesh described by its vertex buffer and index buffer and assumes it is already convex (you need to ensure that it is convex yourself). This will be more efficient than the`Collider::convex_hull`

constructor because it won't perform any calculations to ensure convexity. However, if the input mesh isn't actually convex, the collision-detection for that shape will give an incorrect result.

### Triangle meshes and polylines

Triangle meshes (in 3D) and polylines (in 2D) can be used to describe the boundary of any kind of shape. This is generally useful to describe the fixed environment in games (terrains, buildings, etc.) Triangle meshes and polylines are defined by their vertex buffer and their index buffer. The winding of the triangles of a triangle mesh does not matter. Its topology doesn't matter either (it can have holes, cavities, doesn't need to be closed or manifold). It is however strongly recommended to avoid triangles that are long and thin because they can result in a lower numerical stability of collision-detection.

A triangle mesh/polyline is composed of triangles/segments with no thickness. This means that geometric queries like point-containment tests won't work intuitively because the triangle mesh is assumed to have no interior.

A **triangle-mesh** collider can be built
with `Collider::trimesh(vertices, indices)`

where
`vertices`

is the buffer containing all the vertices of the mesh, and `indices`

is a set of indices indicating what vertex is used by
what triangle. The vertex buffer and index buffer may have different lengths, and any vertex can be shared
by multiple triangles.

A **polyline** collider can be built
with `Collider::polyline(vertices, indices)`

where
`vertices`

is the buffer containing all the vertices of the polyline, and `indices`

is an optional set of indices
indicating what vertex is used by what segment. The vertex buffer and index buffer may have different lengths, and any
vertex can be shared by multiple segments. If the given vertex buffer is `None`

then the input vertices are assumed to
form a line strip, i.e., the polyline is formed from the segments `[vertices[0], vertices[1]], [vertices[1], vertices[2]], etc.`

It is discouraged to use a triangle meshes or a polylines for colliders attached to
dynamic rigid-bodies. Because they have no interior, it is easy for another object to get stuck into them.
In order to simulate properly non-convex objects, it is recommended to use a **convex decomposition** with a
compound shape instead.

### Heightfields

Heightfields are a more restrictive version of triangle-meshes and polylines. However, they can be easier to define and use much less memory. Therefore heightfields are useful to define large parts of terrains with simple topologies.

A **3D heightfield** is basically large rectangle in the `X-Z`

plane, subdivided in a grid pattern at regular intervals. Each
vertex of this subdivision is given a height, i.e., the coordinate of that point along the `Y`

axis. A 3D heightfield
collider can be created
with `Collider::heightfield(heights, scale)`

where
`heights`

is a matrix indicating the altitude of each subdivision point of that heightfield. The number of rows of that
matrix is the number of subdivision along the `X`

axis, and the number of columns is the number of subdivision along the
`Z`

axis. The `scale`

argument indicates the size of the rectangle of the `X-Z`

plane.

A heightfield collider can be given any orientation by changing the orientation of the collider itself.

A **2D heightfield** is a large segment along the `X`

axis, subdivided at regular intervals. Each vertex of this
subdivision is given a height, i.e., the coordinate of that point along the `Y`

axis. A 2D heightfield collider
can be created
with `Collider::heightfield(heights, scale)`

where
`heights`

is a vector indicating the altitude of each subdivision point of that heightfield. The number of elements on
that vector is the number of subdivision of the heightfield. The `scale`

argument indicates the length of the subdivided
segment along the `X`

axis.

### Compound shapes

It is not recommended to use a triangle mesh or polyline for the shape of a collider attached to a dynamic rigid-body.
The alternative is to use a compound shape to model a non-convex object as the union of multiple convex parts (which can
be cuboids, balls, convex meshes, etc.) This is commonly known as a **convex decomposition**.

An alternative to using a **compound shape** is to attach **multiple colliders** to the same rigid-body: all the colliders
will move with the rigid-body automatically, behaving in a very similar way than using a single collider with a
compound shape. The main differences between the two approaches is about collision events: each collider generates
individual collision start/stop events.

To build a compound shape, it is possible to directly provide the set of shapes as well as their position in the compound shape's local space:

`commands.spawn(Collider::compound(vec![`

(pos1, rot1, shape1),

(pos2, rot2, shape2),

]));

It is also possible to build a compound shape modelling the convex decomposition of a 3D triangle mesh or 2D polyline
using the `Collider::convex_decomposition(vertices, indices)`

method. This will automatically create a compound shape
composed of multiple convex meshes obtained from the approximate convex decomposition of the triangle mesh (or polyline in 2D)
using the VHACD algorithm. Here are examples of a 2D concave polygon decomposed into two
convex parts as well as a 3D mesh with its approximate convex decomposition composed of 7 convex parts:

### Round shapes

Some shapes have **round** variants: `RoundCuboid`

, `RoundCylinder`

, `RoundCone`

, `RoundConvexPolygon`

and
`RoundConvexPolyhedron`

. These are shapes to which is added a small thickness with round border:

For algorithmic reasons, collision-detection involving **round cylinders**, **round cones**, **round convex polygon** or
**round convex polyhedron** will be faster than collision-detection with their non-round counterparts. However,
collision-detection with **round-cuboids** will be slower than collision-detection with regular cuboids.

Colliders with round shapes are built in a way very similar to their non-round
counterparts, e.g., `Collider::round_cuboid`

.
These constructors take one additional parameter: the size of the added thickness called `border_radius`

.

## Mass properties

The mass properties of a rigid-body is computed as the sum of the mass-properties
manually set by the user for the rigid-body, plus the **mass-properties of the colliders** attached to it. There are two
ways to define the mass-properties of a collider:

- The
**easiest, automatic, way**: by giving the collider a non-zero density (the default density is 1.0) or a non-zero mass. This will make sure the other mass-properties like the angular inertia tensor are computed automatically from the collider's shape. - The
**manual way**: by giving an explicit mass and angular inertia to the collider.

It is recommended to use the density-based or mass-based approaches as it will ensure the automatically-computed mass-properties are coherent with the geometric shape. Wrong mass-properties (especially the angular inertia part and center-of-mass location) may lead to odd behaviors. The manual approach is usually useful when modeling real-world objects for which you already know the real-world mass, center-of-mass, and angular inertia tensor.

The mass-properties of a collider can only be set when the collider is created:

- Example 2D
- Example 3D

`// First option: by setting the density of the collider (or we could just leave`

// its default value 1.0).

let collider_mprops = ColliderMassProperties::Density(2.0);

// Second option: by setting the mass of the collider.

let collider_mprops = ColliderMassProperties::Mass(0.8);

// Third option: by setting the mass-properties explicitly.

let collider_mprops = ColliderMassProperties::MassProperties(MassProperties {

local_center_of_mass: Vec2::new(0.0, 1.0),

mass: 0.5,

principal_inertia: 0.3,

});

// When the collider is attached, the rigid-body's mass and angular

// inertia will be automatically updated to take the collider into account.

commands

.spawn(RigidBody::Dynamic)

.insert(Collider::ball(0.5))

.insert(collider_mprops);

`// First option: by setting the density of the collider (or we could just leave`

// its default value 1.0).

let collider_mprops = ColliderMassProperties::Density(2.0);

// Second option: by setting the mass of the collider.

let collider_mprops = ColliderMassProperties::Mass(0.8);

// Third option: by setting the mass-properties explicitly.

let collider_mprops = ColliderMassProperties::MassProperties(MassProperties {

local_center_of_mass: Vec3::new(0.0, 1.0, 2.0),

mass: 0.5,

principal_inertia_local_frame: Quat::IDENTITY,

principal_inertia: Vec3::new(0.3, 0.4, 0.5),

});

// When the collider is attached, the rigid-body's mass and angular

// inertia will be automatically updated to take the collider into account.

commands

.spawn(RigidBody::Dynamic)

.insert(Collider::ball(0.5))

.insert(collider_mprops);

## Position

The position of a collider represents its location (translation) in 2D or 3D world-space as well as its orientation (rotation).
Both are combined in a Bevy `Transform`

component.

Please read carefully the paragraph after the next example. It explains how the collider position (and the action of setting this position) behaves differently when it is attached to a rigid-body.

It is possible to set this position when the collider is created or after its creation:

- Example 2D
- Example 3D

`/* Set the collider position when the collider is created. */`

commands

.spawn(Collider::cuboid(0.5, 0.5))

.insert(TransformBundle::from(Transform::from_xyz(1.0, 2.0, 0.0)));

`// Attach the collider to the rigid-body. The collider is attached as its`

// children, so the collider’s `Transform` components sets its position

// relative to the parent rigid-body.

commands

.spawn((RigidBody::Dynamic, GlobalTransform::default()))

.with_children(|children| {

children

.spawn(Collider::cuboid(0.5, 0.5))

.insert(TransformBundle::from(Transform::from_xyz(1.0, 2.0, 0.0)));

});

`/* Set the collider position when the collider is created. */`

commands

.spawn(Collider::cuboid(0.5, 0.5, 0.5))

.insert(TransformBundle::from(Transform::from_xyz(1.0, 2.0, 3.0)));

`/* Set the collider position inside of a system. */`

fn modify_collider_position(mut positions: Query<&mut Transform, With<Collider>>) {

for mut position in positions.iter_mut() {

position.translation.x = 2.0;

}

}

If a collider is **attached to a rigid-body**, its position is automatically updated by the physics pipeline when a
rigid-body is moved by the physics pipeline. If a change to the rigid-body position is made by the user then the collider
position will be updated during the next timestep.

Therefore, setting the `Transform`

of a collider attached to a rigid-body will modify the position of the
collider **relative** to the rigid-body it is attached to:

- Example 2D
- Example 3D

`/* Set the collider position inside of a system. */`

fn modify_collider_position(mut positions: Query<&mut Transform, With<Collider>>) {

for mut position in positions.iter_mut() {

position.translation.x = 2.0;

}

}

`// Attach the collider to the rigid-body. The collider is attached as its`

// children, so the collider’s `Transform` components sets its position

// relative to the parent rigid-body.

commands

.spawn(RigidBody::Dynamic)

.with_children(|children| {

children

.spawn(Collider::cuboid(0.5, 0.5, 0.5))

.insert(TransformBundle::from(Transform::from_xyz(1.0, 2.0, 0.0)));

});

## Friction

Friction is a force that opposes the relative tangential motion between two rigid-bodies with colliders in contact.
This force has a direction orthogonal to the contact normal and opposite to the relative rigid-body motion at
the contact point. Following the Coulomb friction model, the maximum magnitude of this force is the magnitude of the
force along the contact normal multiplied by a **friction coefficient**. A friction coefficient
of 0 implies no friction at all (completely sliding contact) and a coefficient greater or equal to 1 implies a
very strong friction. Values greater than 1 are allowed.

Rapier does not make any distinction between the fixed and dynamic friction coefficients currently.

Each collider has its own friction coefficient. This means that when two colliders are in contact, we need to apply a
rule that combines the friction coefficients of these two colliders into a single coefficient that will be used for
the contact. This rule is described by the `CoefficientCombineRule`

enum:

`CoefficientCombineRule::Average`

: the average of the two coefficients is used for the contact.`CoefficientCombineRule::Min`

: the minimum among the two coefficients is used for the contact.`CoefficientCombineRule::Multiply`

: the product of the two coefficients is used for the contact.`CoefficientCombineRule::Max`

: the maximum among the two coefficients is used for the contact.

By default, the `Average`

rule is used. Each collider can be given its own friction combine rule. When two colliders are in
contact, we need to select one of their combine rule. The following precedence is used: `Max > Multiply > Min > Average`

.

For example if one collider with the `Multiply`

friction combine rule is in contact with a collider with the `Average`

friction
combine rule, then the `Multiply`

rule will be applied for the friction coefficient of this contact (i.e. the coefficients
of both colliders will be multiplied to obtain the coefficient used by the contact).

The `CoefficientCombineRule`

system exists to cover a wide variety of use-cases efficiently. If this is not flexible
enough, it is possible to get full control over the selection of friction coefficients for each contact point using
contact modification. For example, contact modification allows the
simulation of colliders with non-uniform friction coefficients.

The friction coefficient and friction combine rule can both be set when the collider is created or after its creation:

`/* Set the friction coefficient and friction combine rule`

when the collider is created. */

commands.spawn(Collider::ball(0.5)).insert(Friction {

coefficient: 0.7,

combine_rule: CoefficientCombineRule::Min,

});

`/* Set the friction coefficient and friction combine rule`

inside of a system. */

fn modify_collider_friction(mut frictions: Query<&mut Friction>) {

for mut friction in frictions.iter_mut() {

friction.coefficient = 0.7;

friction.combine_rule = CoefficientCombineRule::Min;

}

}

## Restitution

Restitution controls how **elastic** (aka. **bouncy**) a contact is. The elasticity of a contact is controlled
by the **restitution coefficient**. A restitution coefficient set to 1 (fully elastic contact) implies that the
exit velocity at a contact has the same magnitude as the entry velocity along the contact normal: it is as if you drop a
bouncing ball and it gets back to the same height after the bounce. A restitution coefficient set to 0 implies that the
exit velocity at a contact will be zero along the contact normal: it's as if you drop a ball but it doesn't bounce at all.

The friction and restitution coefficients are both managed in very similar ways: with the `CoefficientCombineRule`

or with contact modification. The paragraph bellow is almost identical to the paragraph about friction.

Each collider has its own restitution coefficient. This means that when two colliders are in contact, we need to apply a
rule that combines the restitution coefficients of these two colliders into a single coefficient that will be used for
the contact. This rule is described by the `CoefficientCombineRule`

enum:

`CoefficientCombineRule::Average`

: the average of the two coefficients is used for the contact.`CoefficientCombineRule::Min`

: the minimum among the two coefficients is used for the contact.`CoefficientCombineRule::Multiply`

: the product of the two coefficients is used for the contact.`CoefficientCombineRule::Max`

: the maximum among the two coefficients is used for the contact.

By default, the `Average`

rule is used. Each collider can be given its own restitution combine rule. When two colliders are in
contact, we need to select one of their combine rule. The following precedence is used: `Max > Multiply > Min > Average`

.

For example if one collider with the `Multiply`

restitution combine rule is in contact with a collider with the `Average`

restitution
combine rule, then the `Multiply`

rule will be applied for the restitution coefficient of this contact (i.e. the coefficients
of both colliders will be multiplied to obtain the coefficient used by the contact).

The `CoefficientCombineRule`

system exists to cover a wide variety of use-cases efficiently. If this is not flexible
enough, it is possible to get full control over the selection of restitution coefficients for each contact point using
contact modification. For example, contact modification allows the
simulation of colliders with non-uniform restitution coefficients.

The restitution coefficient and restitution combine rule can both be set when the collider is created or after its creation:

`/* Set the restitution coefficient and restitution combine rule`

when the collider is created. */

commands.spawn(Collider::ball(0.5)).insert(Restitution {

coefficient: 0.7,

combine_rule: CoefficientCombineRule::Min,

});

`/* Set the restitution coefficient and restitution combine rule`

inside of a system. */

fn modify_collider_restitution(mut restitutions: Query<&mut Restitution>) {

for mut restitution in restitutions.iter_mut() {

restitution.coefficient = 0.7;

restitution.combine_rule = CoefficientCombineRule::Min;

}

}

## Collision groups and solver groups

The most efficient way of preventing some pairs of colliders from interacting with each other is to use collision groups or solver groups. Each collider is given:

- A
`collision_groups`

for filtering what pair of colliders should have their contacts (or intersection test if at least one of the colliders is a sensor) computed by the narrow-phase. This filtering happens right after the broad-phase, at the beginning of the narrow phase. - A
`solver_groups`

for filtering what pair of colliders should have their contact forces computed. This filtering happens at the end of the narrow-phase, before the constraints solver.

In other words, the `solver_groups`

is here to prevent contact forces from being computed between some colliders, whereas the
`collision_groups`

will also prevent the contact themselves (and contact events) from being computed. The
`collision_groups`

should be preferred most of the time because it skips more computations. The `solver_groups`

is only
useful if you really want the contact information to be computed but not the forces, for example so that you can apply your
own forces based on these contacts.

A collision group or solver group is described as a pair of bit masks:

- The groups
**membership**indicates what groups the collider is part of (one bit per group). - The groups
**filter**indicates what groups the collider can interact with (one bit per group).

Because the `membership`

and `filter`

bit masks are `u32`

there is a total of 32 groups. By default all bits are set to
1: the collider is part of every group, and can interact with every group.

For example, let's say we want our collider `A`

to be part of the groups `[0, 2, 3]`

and to be able to interact
with the groups `[2]`

, then its groups membership is `0b1101`

and its groups filter is
`0b0100`

. The collision groups and solver groups of a collider can be set
during or after its creation:

`/* Set the collision and/or solver groups when the collider is created. */`

commands

.spawn(Collider::ball(0.5))

.insert(CollisionGroups::new(

Group::GROUP_1 | Group::GROUP_3 | Group::GROUP_4,

Group::GROUP_3,

))

.insert(SolverGroups::new(

Group::GROUP_1 | Group::GROUP_2,

Group::GROUP_1 | Group::GROUP_2 | Group::GROUP_4,

));

`/* Set the collision and/or solver groups inside of a system. */`

fn modify_collider_groups(

mut collision_groups: Query<&mut CollisionGroups>,

mut solver_groups: Query<&mut SolverGroups>,

) {

for mut collision_groups in collision_groups.iter_mut() {

collision_groups.memberships = Group::GROUP_1 | Group::GROUP_3 | Group::GROUP_4;

collision_groups.filters = Group::GROUP_3;

}

for mut solver_groups in solver_groups.iter_mut() {

solver_groups.memberships = Group::GROUP_1 | Group::GROUP_2;

solver_groups.filters = Group::GROUP_1 | Group::GROUP_2 | Group::GROUP_4;

}

}

After the broad-phase detects that two colliders `A`

and `B`

may start being in contact, the narrow-phase will check the collision
groups of both colliders to see if it needs to compute contacts. The check operates as follows:

- If the collider
`A`

is not member of any collision group in the filter of`B`

, then no contact is computed. - If the collider
`B`

is not member of any collision group in the filter of`A`

, then no contact is computed. - The exact bit-wise check is the following:

` (A.collision_groups().memberships & B.collision_groups().filter) != 0`

&& (B.collision_groups().memberships & A.collision_groups().filter) != 0

If this test succeeds, then the narrow-phase will compute the contacts. Then it will check the solver groups of both
colliders, using the same kind of tests as described before but using the `solver_groups`

instead of `collision_groups`

.
If the test succeeds then the constraints solver will compute forces for these contacts. Otherwise, it won't.

## Active collision types

By default, collision-detection is completely disabled between two colliders when both are attached to non-dynamic bodies.
Sometimes, it can be useful to enable collision-detection between, e.g., a collider attached to a kinematic rigid-body
and a collider attached to a fixed rigid-body. This can be done by modifying the collider's `ActiveCollisionTypes`

:

`/* Set the active collision types when the collider is created. */`

commands

.spawn(Collider::ball(0.5))

.insert(ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC);

`/* Set the active collision types inside of a system. */`

fn modify_collider_active_collision_types(mut active_types: Query<&mut ActiveCollisionTypes>) {

for mut active_types in active_types.iter_mut() {

*active_types = (ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC);

}

}

To enable collision-detection between kinematic bodies and fixed bodies (as well as dynamic bodies), set its active collision types to:

`ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_FIXED`

## Active events

Event handlers are user-defined callbacks used to be
notified when two colliders start/stop touching. By default no collision event is generated by
the narrow-phase. In order to enable a collision event for a pair of colliders, at
least one of the involved colliders must have the corresponding event set as active. An event is activated for a collider
by setting its corresponding active events bit to `1`

:

- Setting the
`ActiveEvents::COLLISION_EVENTS`

bit to 1 enables the collision events involving the collider.

The active events of a collider can be set when the collider is created or after its creation:

`/* Set the active events when the collider is created. */`

commands

.spawn(Collider::ball(0.5))

.insert(ActiveEvents::COLLISION_EVENTS);

`/* Set the active events inside of a system. */`

fn modify_collider_active_events(mut active_events: Query<&mut ActiveEvents>) {

for mut active_events in active_events.iter_mut() {

*active_events = ActiveEvents::COLLISION_EVENTS;

}

}

## Active hooks

Physics hooks are user-defined callbacks used to filter-out some contact
pairs, or modify contacts, based on arbitrary user code. In order to enable a physics hook for a pair of colliders, at
least one of the involved colliders must have the corresponding hook set as active. A hook is activated for a collider
by setting its corresponding active hooks bit to `1`

:

- Setting the
`ActiveHooks::FILTER_CONTACT_PAIR`

bit to 1 enables the manual filtering of all the contact pairs involving the collider. - Setting the
`ActiveHooks::FILTER_INTERSECTION_PAIR`

bit to 1 enables the manual filtering of all the contact pairs involving the collider. - Setting the
`ActiveHooks::MODIFY_SOLVER_CONTACTS`

bit to 1 enables the manual contact modification for all the contact manifolds involving the collider.

The active hooks of a collider can be set when the collider is created or after its creation:

`/* Set the active hooks when the collider is created. */`

commands

.spawn(Collider::ball(0.5))

.insert(ActiveHooks::FILTER_CONTACT_PAIRS | ActiveHooks::MODIFY_SOLVER_CONTACTS);

`/* Set the active hooks inside of a system. */`

fn modify_collider_active_hooks(mut active_hooks: Query<&mut ActiveHooks>) {

for mut active_hooks in active_hooks.iter_mut() {

*active_hooks = ActiveHooks::FILTER_CONTACT_PAIRS | ActiveHooks::MODIFY_SOLVER_CONTACTS;

}

}