avian3d/collision/collider/collider_hierarchy/
mod.rs1mod 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#[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 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 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 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 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#[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>);