avian2d/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_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 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 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 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 let collider_transform = collider_global_transform.reparented_to(body_global_transform);
103
104 *world.get_mut::<ColliderTransform>(entity).unwrap() =
106 ColliderTransform::from(collider_transform);
107
108 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 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 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 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#[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}