Skip to content

Commit 5eb5722

Browse files
authored
Add ColliderDisabled (#584)
# Objective Closes #436. Follow-up to #536. Avian already supports disabling rigid bodies (#536) and joints (#519). The missing piece is disabling colliders! ## Solution Add a `ColliderDisabled` component for temporarily disabling collision detection for colliders and removing them from spatial queries. For now, I decided *not* to prevent disabled colliders from contributing to mass properties of rigid bodies, since the primary purpose of the component is to temporarily disable collision detection, and I think it would be unexpected if it affected the behavior of the rigid body in any other way.
1 parent 114554b commit 5eb5722

File tree

6 files changed

+93
-31
lines changed

6 files changed

+93
-31
lines changed

src/collision/broad_phase.rs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
66
use crate::prelude::*;
77
use bevy::{
8-
ecs::entity::{EntityMapper, MapEntities},
8+
ecs::{
9+
entity::{EntityMapper, MapEntities},
10+
system::lifetimeless::Read,
11+
},
912
prelude::*,
1013
};
1114

@@ -109,13 +112,16 @@ impl MapEntities for AabbIntervals {
109112
/// Updates [`AabbIntervals`] to keep them in sync with the [`ColliderAabb`]s.
110113
#[allow(clippy::type_complexity)]
111114
fn update_aabb_intervals(
112-
aabbs: Query<(
113-
&ColliderAabb,
114-
Option<&ColliderParent>,
115-
Option<&CollisionLayers>,
116-
Has<AabbIntersections>,
117-
Has<Sleeping>,
118-
)>,
115+
aabbs: Query<
116+
(
117+
&ColliderAabb,
118+
Option<&ColliderParent>,
119+
Option<&CollisionLayers>,
120+
Has<AabbIntersections>,
121+
Has<Sleeping>,
122+
),
123+
Without<ColliderDisabled>,
124+
>,
119125
rbs: Query<&RigidBody>,
120126
mut intervals: ResMut<AabbIntervals>,
121127
) {
@@ -145,25 +151,26 @@ fn update_aabb_intervals(
145151
);
146152
}
147153

154+
type AabbIntervalQueryData = (
155+
Entity,
156+
Option<Read<ColliderParent>>,
157+
Read<ColliderAabb>,
158+
Option<Read<RigidBody>>,
159+
Option<Read<CollisionLayers>>,
160+
Has<AabbIntersections>,
161+
);
162+
148163
/// Adds new [`ColliderAabb`]s to [`AabbIntervals`].
149164
#[allow(clippy::type_complexity)]
150165
fn add_new_aabb_intervals(
151-
aabbs: Query<
152-
(
153-
Entity,
154-
Option<&ColliderParent>,
155-
&ColliderAabb,
156-
Option<&RigidBody>,
157-
Option<&CollisionLayers>,
158-
Has<AabbIntersections>,
159-
),
160-
Added<ColliderAabb>,
161-
>,
166+
added_aabbs: Query<AabbIntervalQueryData, (Added<ColliderAabb>, Without<ColliderDisabled>)>,
167+
aabbs: Query<AabbIntervalQueryData>,
162168
mut intervals: ResMut<AabbIntervals>,
169+
mut re_enabled_colliders: RemovedComponents<ColliderDisabled>,
163170
) {
164-
let aabbs = aabbs
165-
.iter()
166-
.map(|(ent, parent, aabb, rb, layers, store_intersections)| {
171+
let re_enabled_aabbs = aabbs.iter_many(re_enabled_colliders.read());
172+
let aabbs = added_aabbs.iter().chain(re_enabled_aabbs).map(
173+
|(ent, parent, aabb, rb, layers, store_intersections)| {
167174
(
168175
ent,
169176
parent.map_or(ColliderParent(ent), |p| *p),
@@ -173,7 +180,8 @@ fn add_new_aabb_intervals(
173180
rb.map_or(false, |rb| rb.is_static()),
174181
store_intersections,
175182
)
176-
});
183+
},
184+
);
177185
intervals.0.extend(aabbs);
178186
}
179187

src/collision/collider/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,51 @@ pub trait ScalableCollider: AnyCollider {
106106
}
107107
}
108108

109+
/// A marker component that indicates that a [collider](Collider) is disabled
110+
/// and should not detect collisions or be included in spatial queries.
111+
///
112+
/// This is useful for temporarily disabling a collider without removing it from the world.
113+
/// To re-enable the collider, simply remove the component.
114+
///
115+
/// Note that a disabled collider will still contribute to the mass properties of the rigid body
116+
/// it is attached to. Set the [`Mass`] of the collider to zero to prevent this.
117+
///
118+
/// # Example
119+
///
120+
/// ```
121+
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
122+
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
123+
/// # use bevy::prelude::*;
124+
/// #
125+
/// #[derive(Component)]
126+
/// pub struct Character;
127+
///
128+
/// /// Disables colliders for all rigid body characters, for example during cutscenes.
129+
/// fn disable_character_colliders(
130+
/// mut commands: Commands,
131+
/// query: Query<Entity, (With<RigidBody>, With<Character>)>,
132+
/// ) {
133+
/// for entity in &query {
134+
/// commands.entity(entity).insert(ColliderDisabled);
135+
/// }
136+
/// }
137+
///
138+
/// /// Enables colliders for all rigid body characters.
139+
/// fn enable_character_colliders(
140+
/// mut commands: Commands,
141+
/// query: Query<Entity, (With<RigidBody>, With<Character>)>,
142+
/// ) {
143+
/// for entity in &query {
144+
/// commands.entity(entity).remove::<ColliderDisabled>();
145+
/// }
146+
/// }
147+
/// ```
148+
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)]
149+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
150+
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
151+
#[reflect(Debug, Component, Default, PartialEq)]
152+
pub struct ColliderDisabled;
153+
109154
/// A component that stores the `Entity` ID of the [`RigidBody`] that a [`Collider`] is attached to.
110155
///
111156
/// If the collider is a child of a rigid body, this points to the body's `Entity` ID.

src/collision/narrow_phase.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ fn generate_constraints<C: AnyCollider>(
365365
#[derive(SystemParam)]
366366
pub struct NarrowPhase<'w, 's, C: AnyCollider> {
367367
parallel_commands: ParallelCommands<'w, 's>,
368-
collider_query: Query<'w, 's, ColliderQuery<C>>,
368+
collider_query: Query<'w, 's, ColliderQuery<C>, Without<ColliderDisabled>>,
369369
body_query: Query<
370370
'w,
371371
's,

src/dynamics/sleeping/mod.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,11 @@ fn wake_on_collision_ended(
280280
Without<Sleeping>,
281281
),
282282
>,
283-
colliders: Query<(&ColliderParent, Ref<ColliderTransform>)>,
283+
colliders: Query<(
284+
&ColliderParent,
285+
Ref<ColliderTransform>,
286+
Has<ColliderDisabled>,
287+
)>,
284288
collisions: Res<Collisions>,
285289
mut sleeping: Query<(Entity, &mut TimeSleeping, Has<Sleeping>)>,
286290
) {
@@ -303,12 +307,15 @@ fn wake_on_collision_ended(
303307
}
304308
});
305309
if colliding_entities.any(|other_entity| {
306-
colliders.get(other_entity).is_ok_and(|(p, transform)| {
307-
transform.is_changed()
308-
|| bodies
309-
.get(p.get())
310-
.is_ok_and(|(pos, is_disabled)| is_disabled || pos.is_changed())
311-
})
310+
colliders
311+
.get(other_entity)
312+
.is_ok_and(|(p, transform, is_collider_disabled)| {
313+
is_collider_disabled
314+
|| transform.is_changed()
315+
|| bodies
316+
.get(p.get())
317+
.is_ok_and(|(pos, is_rb_disabled)| is_rb_disabled || pos.is_changed())
318+
})
312319
}) {
313320
if is_sleeping {
314321
commands.entity(entity).remove::<Sleeping>();

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
//! - [Collision events](ContactReportingPlugin#collision-events)
153153
//! - [Accessing, filtering and modifying collisions](Collisions)
154154
//! - [Manual contact queries](contact_query)
155+
//! - [Temporarily disabling a collider](ColliderDisabled)
155156
//!
156157
//! See the [`collision`] module for more details about collision detection and colliders in Avian.
157158
//!

src/spatial_query/system_param.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub struct SpatialQuery<'w, 's> {
6767
&'static Collider,
6868
Option<&'static CollisionLayers>,
6969
),
70+
Without<ColliderDisabled>,
7071
>,
7172
pub(crate) added_colliders: Query<'w, 's, Entity, Added<Collider>>,
7273
/// The [`SpatialQueryPipeline`].

0 commit comments

Comments
 (0)