avian3d/collision/collider/collider_hierarchy/
mod.rs1mod 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#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
27#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
28#[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 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
67impl 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 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 let collider_transform = collider_global_transform.reparented_to(body_global_transform);
105
106 *world.get_mut::<ColliderTransform>(entity).unwrap() =
108 ColliderTransform::from(collider_transform);
109
110 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 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 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#[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}