avian2d/collision/collider/collider_hierarchy/
mod.rs

1//! [`ColliderOf`] relationships for attaching colliders to rigid bodies
2//! based on the entity hierarchy.
3
4mod plugin;
5
6pub use plugin::ColliderHierarchyPlugin;
7
8use crate::prelude::*;
9use bevy::{
10    ecs::{
11        lifecycle::HookContext,
12        relationship::{Relationship, RelationshipHookMode, RelationshipSourceCollection},
13        world::DeferredWorld,
14    },
15    prelude::*,
16};
17
18/// A [`Relationship`] component that attaches a [`Collider`] to a [`RigidBody`].
19///
20/// Unless manually specified, the [`ColliderOf`] component is automatically initialized
21/// with the nearest rigid body up the chain in the entity hierarchy.
22///
23/// For example, given the following entities:
24///
25/// ```
26#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
27#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
28/// # use bevy::prelude::*;
29/// # fn setup(mut commands: Commands) {
30/// commands.spawn((
31///     RigidBody::Dynamic,
32///     Collider::capsule(0.5, 1.5),
33///     children![
34///         (Collider::capsule(0.5, 1.5), Transform::from_xyz(-2.0, 0.0, 0.0)),
35///         (Collider::capsule(0.5, 1.5), Transform::from_xyz(2.0, 0.0, 0.0)),
36///     ],
37/// ));
38/// # }
39/// ```
40///
41/// all three colliders will be attached to the same rigid body.
42///
43/// However, it also possible to explicitly specify which rigid body a collider is attached to
44/// by inserting the [`ColliderOf`] component manually.
45///
46/// [`Relationship`]: bevy::ecs::relationship::Relationship
47#[derive(Component, Clone, Copy, Debug, PartialEq, Eq, Reflect)]
48#[component(immutable, on_insert = <ColliderOf as Relationship>::on_insert, on_replace = <ColliderOf as Relationship>::on_replace)]
49#[require(ColliderTransform)]
50#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
51#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
52#[reflect(Debug, Component, PartialEq)]
53pub struct ColliderOf {
54    /// The [`Entity`] ID of the [`RigidBody`] that this collider is attached to.
55    pub body: Entity,
56}
57
58impl FromWorld for ColliderOf {
59    #[inline(always)]
60    fn from_world(_world: &mut World) -> Self {
61        ColliderOf {
62            body: Entity::PLACEHOLDER,
63        }
64    }
65}
66
67// Bevy does not currently allow relationships that point to their own entity,
68// so we implement the relationship manually to work around this limitation.
69impl Relationship for ColliderOf {
70    type RelationshipTarget = RigidBodyColliders;
71
72    fn get(&self) -> Entity {
73        self.body
74    }
75
76    fn from(entity: Entity) -> Self {
77        ColliderOf { body: entity }
78    }
79
80    fn on_insert(
81        mut world: DeferredWorld,
82        HookContext {
83            entity,
84            caller,
85            relationship_hook_mode,
86            ..
87        }: HookContext,
88    ) {
89        let collider_ref = world.entity(entity);
90
91        let &ColliderOf { body } = collider_ref.get::<ColliderOf>().unwrap();
92
93        // Get the global transform of the collider and its rigid body.
94        let Some(collider_global_transform) = collider_ref.get::<GlobalTransform>() else {
95            return;
96        };
97        let Some(body_global_transform) = world.get::<GlobalTransform>(body) else {
98            return;
99        };
100
101        // Get the collider's transform relative to the rigid body.
102        let collider_transform = collider_global_transform.reparented_to(body_global_transform);
103
104        // Update the collider transform.
105        *world.get_mut::<ColliderTransform>(entity).unwrap() =
106            ColliderTransform::from(collider_transform);
107
108        // The rest is largely the same as the default implementation,
109        // but allows relationships to point to their own entity.
110
111        match relationship_hook_mode {
112            RelationshipHookMode::Run => {}
113            RelationshipHookMode::Skip => return,
114            RelationshipHookMode::RunIfNotLinked => {
115                if RigidBodyColliders::LINKED_SPAWN {
116                    return;
117                }
118            }
119        }
120
121        let collider = entity;
122        let body = world.entity(collider).get::<ColliderOf>().unwrap().body;
123
124        if let Some(mut body_mut) = world
125            .get_entity_mut(body)
126            .ok()
127            .filter(|e| e.contains::<RigidBody>())
128        {
129            // Attach the collider to the rigid body.
130            if let Some(mut colliders) = body_mut.get_mut::<RigidBodyColliders>() {
131                colliders.0.push(collider);
132            } else {
133                world
134                    .commands()
135                    .entity(body)
136                    .insert(RigidBodyColliders(vec![collider]));
137            }
138        } else {
139            warn!(
140                "{}Tried to attach collider on entity {collider} to rigid body on entity {body}, but the rigid body does not exist.",
141                caller
142                    .map(|location| format!("{location}: "))
143                    .unwrap_or_default(),
144            );
145        }
146    }
147
148    fn on_replace(
149        mut world: DeferredWorld,
150        HookContext {
151            entity,
152            relationship_hook_mode,
153            ..
154        }: HookContext,
155    ) {
156        // This is largely the same as the default implementation,
157        // but does not panic if the relationship target does not exist.
158
159        match relationship_hook_mode {
160            RelationshipHookMode::Run => {}
161            RelationshipHookMode::Skip => return,
162            RelationshipHookMode::RunIfNotLinked => {
163                if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
164                    return;
165                }
166            }
167        }
168        let body = world.entity(entity).get::<Self>().unwrap().get();
169        if let Ok(mut body_mut) = world.get_entity_mut(body)
170            && let Some(mut relationship_target) = body_mut.get_mut::<Self::RelationshipTarget>()
171        {
172            RelationshipSourceCollection::remove(
173                relationship_target.collection_mut_risky(),
174                entity,
175            );
176            if relationship_target.is_empty()
177                && let Ok(mut entity) = world.commands().get_entity(body)
178            {
179                // this "remove" operation must check emptiness because in the event that an identical
180                // relationship is inserted on top, this despawn would result in the removal of that identical
181                // relationship ... not what we want!
182                entity.queue_handled(
183                    |mut entity: EntityWorldMut| {
184                        if entity
185                            .get::<Self::RelationshipTarget>()
186                            .is_some_and(RelationshipTarget::is_empty)
187                        {
188                            entity.remove::<Self::RelationshipTarget>();
189                        }
190                    },
191                    |_, _| {},
192                );
193            }
194        }
195    }
196
197    fn set_risky(&mut self, entity: Entity) {
198        self.body = entity;
199    }
200}
201
202/// A [`RelationshipTarget`] component that tracks which colliders are attached to a [`RigidBody`].
203///
204/// This is automatically inserted and pupulated with entities that are attached to a rigid body
205/// using the [`ColliderOf`] [`Relationship`] component.
206///
207/// You should not modify this component directly to avoid desynchronization.
208/// Instead, modify the [`ColliderOf`] components on the colliders.
209///
210/// [`Relationship`]: bevy::ecs::relationship::Relationship
211#[derive(Component, Clone, Debug, Default, PartialEq, Reflect)]
212#[relationship_target(relationship = ColliderOf, linked_spawn)]
213#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
214#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
215#[reflect(Debug, Component, Default, PartialEq)]
216pub struct RigidBodyColliders(Vec<Entity>);
217
218impl<'a> IntoIterator for &'a RigidBodyColliders {
219    type Item = <Self::IntoIter as Iterator>::Item;
220
221    type IntoIter = core::iter::Copied<core::slice::Iter<'a, Entity>>;
222
223    #[inline(always)]
224    fn into_iter(self) -> Self::IntoIter {
225        self.0.iter()
226    }
227}
228
229impl core::ops::Deref for RigidBodyColliders {
230    type Target = [Entity];
231
232    fn deref(&self) -> &Self::Target {
233        &self.0
234    }
235}