avian3d/collision/collider/collider_hierarchy/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! [`ColliderOf`] relationships for attaching colliders to rigid bodies
//! based on the entity hierarchy.
mod plugin;
pub use plugin::ColliderHierarchyPlugin;
use crate::prelude::*;
use bevy::{
ecs::{
component::HookContext,
relationship::{Relationship, RelationshipHookMode, RelationshipSourceCollection},
world::DeferredWorld,
},
prelude::*,
};
/// A [`Relationship`] component that attaches a [`Collider`] to a [`RigidBody`].
///
/// Unless manually specified, the [`ColliderOf`] component is automatically initialized
/// with the nearest rigid body up the chain in the entity hierarchy.
///
/// For example, given the following entities:
///
/// ```
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
/// # use bevy::prelude::*;
/// # fn setup(mut commands: Commands) {
/// commands.spawn((
/// RigidBody::Dynamic,
/// Collider::capsule(0.5, 1.5),
/// children![
/// (Collider::capsule(0.5, 1.5), Transform::from_xyz(-2.0, 0.0, 0.0)),
/// (Collider::capsule(0.5, 1.5), Transform::from_xyz(2.0, 0.0, 0.0)),
/// ],
/// ));
/// # }
/// ```
///
/// all three colliders will be attached to the same rigid body.
///
/// However, it also possible to explicitly specify which rigid body a collider is attached to
/// by inserting the [`ColliderOf`] component manually.
///
/// [`Relationship`]: bevy::ecs::relationship::Relationship
#[derive(Component, Clone, Copy, Debug, PartialEq, Eq, Reflect)]
#[component(immutable, on_insert = <ColliderOf as Relationship>::on_insert, on_replace = <ColliderOf as Relationship>::on_replace)]
#[require(ColliderTransform)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, PartialEq)]
pub struct ColliderOf {
/// The [`Entity`] ID of the [`RigidBody`] that this collider is attached to.
pub body: Entity,
}
impl FromWorld for ColliderOf {
#[inline(always)]
fn from_world(_world: &mut World) -> Self {
ColliderOf {
body: Entity::PLACEHOLDER,
}
}
}
// Bevy does not currently allow relationships that point to their own entity,
// so we implement the relationship manually to work around this limitation.
impl Relationship for ColliderOf {
type RelationshipTarget = RigidBodyColliders;
fn get(&self) -> Entity {
self.body
}
fn from(entity: Entity) -> Self {
ColliderOf { body: entity }
}
fn on_insert(
mut world: DeferredWorld,
HookContext {
entity,
caller,
relationship_hook_mode,
..
}: HookContext,
) {
// This is largely the same as the default implementation,
// but allows relationships to point to their own entity.
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip => return,
RelationshipHookMode::RunIfNotLinked => {
if RigidBodyColliders::LINKED_SPAWN {
return;
}
}
}
let collider = entity;
let body = world.entity(collider).get::<ColliderOf>().unwrap().body;
if let Some(mut body_mut) = world
.get_entity_mut(body)
.ok()
.filter(|e| e.contains::<RigidBody>())
{
// Attach the collider to the rigid body.
if let Some(mut colliders) = body_mut.get_mut::<RigidBodyColliders>() {
colliders.0.push(collider);
} else {
world
.commands()
.entity(body)
.insert(RigidBodyColliders(vec![collider]));
}
} else {
warn!(
"{}Tried to attach collider on entity {collider} to rigid body on entity {body}, but the rigid body does not exist.",
caller.map(|location| format!("{location}: ")).unwrap_or_default(),
);
}
}
fn on_replace(
mut world: DeferredWorld,
HookContext {
entity,
relationship_hook_mode,
..
}: HookContext,
) {
// This is largely the same as the default implementation,
// but does not panic if the relationship target does not exist.
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip => return,
RelationshipHookMode::RunIfNotLinked => {
if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
return;
}
}
}
let body = world.entity(entity).get::<Self>().unwrap().get();
if let Ok(mut body_mut) = world.get_entity_mut(body) {
if let Some(mut relationship_target) = body_mut.get_mut::<Self::RelationshipTarget>() {
RelationshipSourceCollection::remove(
relationship_target.collection_mut_risky(),
entity,
);
if relationship_target.len() == 0 {
if let Ok(mut entity) = world.commands().get_entity(body) {
// this "remove" operation must check emptiness because in the event that an identical
// relationship is inserted on top, this despawn would result in the removal of that identical
// relationship ... not what we want!
entity.queue_handled(
|mut entity: EntityWorldMut| {
if entity
.get::<Self::RelationshipTarget>()
.is_some_and(RelationshipTarget::is_empty)
{
entity.remove::<Self::RelationshipTarget>();
}
},
|_, _| {},
);
}
}
}
}
}
}
/// A [`RelationshipTarget`] component that tracks which colliders are attached to a [`RigidBody`].
///
/// This is automatically inserted and pupulated with entities that are attached to a rigid body
/// using the [`ColliderOf`] [`Relationship`] component.
///
/// You should not modify this component directly to avoid desynchronization.
/// Instead, modify the [`ColliderOf`] components on the colliders.
///
/// [`Relationship`]: bevy::ecs::relationship::Relationship
#[derive(Component, Clone, Debug, Default, PartialEq, Reflect)]
#[relationship_target(relationship = ColliderOf, linked_spawn)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, Default, PartialEq)]
pub struct RigidBodyColliders(Vec<Entity>);