collider_collision_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_groupsfor 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_groupsfor 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).
The collision groups and solver groups are given by an InteractionGroups containing both bit masks, as its
memberships and filter attributes, as well as a test_mode explained at the end of this section. Each bit mask is
a Group, built by combining the Group.GROUP_0 to Group.GROUP_31 flags with the | operator.
Because the memberships and filter bit masks are 32-bits there is a total of 32 groups. By default all bits are set
to 1 (Group.ALL): 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 (i.e., Group.GROUP_0 | Group.GROUP_2 | Group.GROUP_3) and its groups filter is 0b0100 (i.e., Group.GROUP_2). The collision groups and solver groups of a collider can be set
during or after its creation:
# Set the collision groups and solver groups when the collider is created.
collider = (
rp.Collider.ball(0.5)
.collision_groups(
rp.InteractionGroups(
memberships=rp.Group.GROUP_1 | rp.Group.GROUP_3 | rp.Group.GROUP_4,
filter=rp.Group.GROUP_3,
test_mode=rp.InteractionTestMode.AND,
)
)
.solver_groups(
rp.InteractionGroups(
memberships=rp.Group.GROUP_1 | rp.Group.GROUP_2,
filter=rp.Group.GROUP_1 | rp.Group.GROUP_2 | rp.Group.GROUP_4,
test_mode=rp.InteractionTestMode.AND,
)
)
.build()
)
# Set the collision groups and solver groups after the collider creation.
collider = world.colliders[collider_handle]
collision_groups = rp.InteractionGroups(
memberships=rp.Group.GROUP_1 | rp.Group.GROUP_3 | rp.Group.GROUP_4,
filter=rp.Group.GROUP_3,
test_mode=rp.InteractionTestMode.AND,
)
solver_groups = rp.InteractionGroups(
memberships=rp.Group.GROUP_1 | rp.Group.GROUP_2,
filter=rp.Group.GROUP_1 | rp.Group.GROUP_2 | rp.Group.GROUP_4,
test_mode=rp.InteractionTestMode.AND,
)
collider.collision_groups = collision_groups
collider.solver_groups = solver_groups
assert collider.collision_groups == collision_groups
assert collider.solver_groups == solver_groups
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
Ais not member of any collision group in the filter ofB, then no contact is computed. - If the collider
Bis not member of any collision group in the filter ofA, then no contact is computed. - The exact bit-wise check is the following:
(a.collision_groups.memberships & b.collision_groups.filter).bits != 0
and (b.collision_groups.memberships & a.collision_groups.filter).bits != 0
This test is also performed by InteractionGroups.test, e.g., a.collision_groups.test(b.collision_groups).
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.
This is the behavior of the default InteractionTestMode.AND test mode (the test_mode of InteractionGroups). If
both colliders use the InteractionTestMode.OR test mode, then satisfying only one of these two conditions is enough
for the contacts (or forces) to be computed.