mod plugin;
pub use plugin::ColliderHierarchyPlugin;
use crate::prelude::*;
use bevy::{
ecs::{
lifecycle::HookContext,
relationship::{Relationship, RelationshipHookMode, RelationshipSourceCollection},
world::DeferredWorld,
},
prelude::*,
};
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
#[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 {
pub body: Entity,
}
impl FromWorld for ColliderOf {
#[inline(always)]
fn from_world(_world: &mut World) -> Self {
ColliderOf {
body: Entity::PLACEHOLDER,
}
}
}
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,
) {
let collider_ref = world.entity(entity);
let &ColliderOf { body } = collider_ref.get::<ColliderOf>().unwrap();
let Some(collider_global_transform) = collider_ref.get::<GlobalTransform>() else {
return;
};
let Some(body_global_transform) = world.get::<GlobalTransform>(body) else {
return;
};
let collider_transform = collider_global_transform.reparented_to(body_global_transform);
*world.get_mut::<ColliderTransform>(entity).unwrap() =
ColliderTransform::from(collider_transform);
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>())
{
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,
) {
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)
&& let Some(mut relationship_target) = body_mut.get_mut::<Self::RelationshipTarget>()
{
RelationshipSourceCollection::remove(
relationship_target.collection_mut_risky(),
entity,
);
if relationship_target.is_empty()
&& let Ok(mut entity) = world.commands().get_entity(body)
{
entity.queue_handled(
|mut entity: EntityWorldMut| {
if entity
.get::<Self::RelationshipTarget>()
.is_some_and(RelationshipTarget::is_empty)
{
entity.remove::<Self::RelationshipTarget>();
}
},
|_, _| {},
);
}
}
}
fn set_risky(&mut self, entity: Entity) {
self.body = entity;
}
}
#[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>);
impl<'a> IntoIterator for &'a RigidBodyColliders {
type Item = <Self::IntoIter as Iterator>::Item;
type IntoIter = core::iter::Copied<core::slice::Iter<'a, Entity>>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl core::ops::Deref for RigidBodyColliders {
type Target = [Entity];
fn deref(&self) -> &Self::Target {
&self.0
}
}