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        component::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        // This is largely the same as the default implementation,
90        // but allows relationships to point to their own entity.
91
92        match relationship_hook_mode {
93            RelationshipHookMode::Run => {}
94            RelationshipHookMode::Skip => return,
95            RelationshipHookMode::RunIfNotLinked => {
96                if RigidBodyColliders::LINKED_SPAWN {
97                    return;
98                }
99            }
100        }
101
102        let collider = entity;
103        let body = world.entity(collider).get::<ColliderOf>().unwrap().body;
104
105        if let Some(mut body_mut) = world
106            .get_entity_mut(body)
107            .ok()
108            .filter(|e| e.contains::<RigidBody>())
109        {
110            // Attach the collider to the rigid body.
111            if let Some(mut colliders) = body_mut.get_mut::<RigidBodyColliders>() {
112                colliders.0.push(collider);
113            } else {
114                world
115                    .commands()
116                    .entity(body)
117                    .insert(RigidBodyColliders(vec![collider]));
118            }
119        } else {
120            warn!(
121                "{}Tried to attach collider on entity {collider} to rigid body on entity {body}, but the rigid body does not exist.",
122                caller.map(|location| format!("{location}: ")).unwrap_or_default(),
123            );
124        }
125    }
126
127    fn on_replace(
128        mut world: DeferredWorld,
129        HookContext {
130            entity,
131            relationship_hook_mode,
132            ..
133        }: HookContext,
134    ) {
135        // This is largely the same as the default implementation,
136        // but does not panic if the relationship target does not exist.
137
138        match relationship_hook_mode {
139            RelationshipHookMode::Run => {}
140            RelationshipHookMode::Skip => return,
141            RelationshipHookMode::RunIfNotLinked => {
142                if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
143                    return;
144                }
145            }
146        }
147        let body = world.entity(entity).get::<Self>().unwrap().get();
148        if let Ok(mut body_mut) = world.get_entity_mut(body) {
149            if let Some(mut relationship_target) = body_mut.get_mut::<Self::RelationshipTarget>() {
150                RelationshipSourceCollection::remove(
151                    relationship_target.collection_mut_risky(),
152                    entity,
153                );
154                if relationship_target.len() == 0 {
155                    if let Ok(mut entity) = world.commands().get_entity(body) {
156                        // this "remove" operation must check emptiness because in the event that an identical
157                        // relationship is inserted on top, this despawn would result in the removal of that identical
158                        // relationship ... not what we want!
159                        entity.queue_handled(
160                            |mut entity: EntityWorldMut| {
161                                if entity
162                                    .get::<Self::RelationshipTarget>()
163                                    .is_some_and(RelationshipTarget::is_empty)
164                                {
165                                    entity.remove::<Self::RelationshipTarget>();
166                                }
167                            },
168                            |_, _| {},
169                        );
170                    }
171                }
172            }
173        }
174    }
175}
176
177/// A [`RelationshipTarget`] component that tracks which colliders are attached to a [`RigidBody`].
178///
179/// This is automatically inserted and pupulated with entities that are attached to a rigid body
180/// using the [`ColliderOf`] [`Relationship`] component.
181///
182/// You should not modify this component directly to avoid desynchronization.
183/// Instead, modify the [`ColliderOf`] components on the colliders.
184///
185/// [`Relationship`]: bevy::ecs::relationship::Relationship
186#[derive(Component, Clone, Debug, Default, PartialEq, Reflect)]
187#[relationship_target(relationship = ColliderOf, linked_spawn)]
188#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
189#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
190#[reflect(Debug, Component, Default, PartialEq)]
191pub struct RigidBodyColliders(Vec<Entity>);