Skip to main content

avian3d/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_discard = <ColliderOf as Relationship>::on_discard)]
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    const ALLOW_SELF_REFERENTIAL: bool = true;
73
74    fn get(&self) -> Entity {
75        self.body
76    }
77
78    fn from(entity: Entity) -> Self {
79        ColliderOf { body: entity }
80    }
81
82    fn on_insert(
83        mut world: DeferredWorld,
84        HookContext {
85            entity,
86            caller,
87            relationship_hook_mode,
88            ..
89        }: HookContext,
90    ) {
91        let collider_ref = world.entity(entity);
92
93        let &ColliderOf { body } = collider_ref.get::<ColliderOf>().unwrap();
94
95        // Get the global transform of the collider and its rigid body.
96        let Some(collider_global_transform) = collider_ref.get::<GlobalTransform>() else {
97            return;
98        };
99        let Some(body_global_transform) = world.get::<GlobalTransform>(body) else {
100            return;
101        };
102
103        // Get the collider's transform relative to the rigid body.
104        let collider_transform = collider_global_transform.reparented_to(body_global_transform);
105
106        // Update the collider transform.
107        *world.get_mut::<ColliderTransform>(entity).unwrap() =
108            ColliderTransform::from(collider_transform);
109
110        // The rest is largely the same as the default implementation,
111        // but allows relationships to point to their own entity.
112
113        match relationship_hook_mode {
114            RelationshipHookMode::Run => {}
115            RelationshipHookMode::Skip => return,
116            RelationshipHookMode::RunIfNotLinked => {
117                if RigidBodyColliders::LINKED_SPAWN {
118                    return;
119                }
120            }
121        }
122
123        let collider = entity;
124        let body = world.entity(collider).get::<ColliderOf>().unwrap().body;
125
126        if let Some(mut body_mut) = world
127            .get_entity_mut(body)
128            .ok()
129            .filter(|e| e.contains::<RigidBody>())
130        {
131            // Attach the collider to the rigid body.
132            if let Some(mut colliders) = body_mut.get_mut::<RigidBodyColliders>() {
133                colliders.0.push(collider);
134            } else {
135                world
136                    .commands()
137                    .entity(body)
138                    .insert(RigidBodyColliders(vec![collider]));
139            }
140        } else {
141            warn!(
142                "{}Tried to attach collider on entity {collider} to rigid body on entity {body}, but the rigid body does not exist.",
143                caller
144                    .map(|location| format!("{location}: "))
145                    .unwrap_or_default(),
146            );
147        }
148    }
149
150    fn on_discard(
151        mut world: DeferredWorld,
152        HookContext {
153            entity,
154            relationship_hook_mode,
155            ..
156        }: HookContext,
157    ) {
158        // This is largely the same as the default implementation,
159        // but does not panic if the relationship target does not exist.
160
161        match relationship_hook_mode {
162            RelationshipHookMode::Run => {}
163            RelationshipHookMode::Skip => return,
164            RelationshipHookMode::RunIfNotLinked => {
165                if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
166                    return;
167                }
168            }
169        }
170        let body = world.entity(entity).get::<Self>().unwrap().get();
171        if let Ok(mut body_mut) = world.get_entity_mut(body)
172            && let Some(mut relationship_target) = body_mut.get_mut::<Self::RelationshipTarget>()
173        {
174            RelationshipSourceCollection::remove(
175                relationship_target.collection_mut_risky(),
176                entity,
177            );
178            if relationship_target.is_empty() {
179                let command = |mut entity: EntityWorldMut| {
180                    if entity
181                        .get::<Self::RelationshipTarget>()
182                        .is_some_and(RelationshipTarget::is_empty)
183                    {
184                        entity.remove::<Self::RelationshipTarget>();
185                    }
186                };
187
188                world.commands().queue_silenced(command.with_entity(body));
189            }
190        }
191    }
192
193    fn set_risky(&mut self, entity: Entity) {
194        self.body = entity;
195    }
196}
197
198/// A [`RelationshipTarget`] component that tracks which colliders are attached to a [`RigidBody`].
199///
200/// This is automatically inserted and pupulated with entities that are attached to a rigid body
201/// using the [`ColliderOf`] [`Relationship`] component.
202///
203/// You should not modify this component directly to avoid desynchronization.
204/// Instead, modify the [`ColliderOf`] components on the colliders.
205///
206/// [`Relationship`]: bevy::ecs::relationship::Relationship
207#[derive(Component, Clone, Debug, Default, PartialEq, Reflect)]
208#[relationship_target(relationship = ColliderOf, linked_spawn)]
209#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
210#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
211#[reflect(Debug, Component, Default, PartialEq)]
212pub struct RigidBodyColliders(Vec<Entity>);
213
214impl<'a> IntoIterator for &'a RigidBodyColliders {
215    type Item = <Self::IntoIter as Iterator>::Item;
216
217    type IntoIter = core::iter::Copied<core::slice::Iter<'a, Entity>>;
218
219    #[inline(always)]
220    fn into_iter(self) -> Self::IntoIter {
221        self.0.iter()
222    }
223}
224
225impl core::ops::Deref for RigidBodyColliders {
226    type Target = [Entity];
227
228    fn deref(&self) -> &Self::Target {
229        &self.0
230    }
231}