use crate::{ancestor_marker::AncestorMarker, prelude::*};
use bevy::prelude::*;
pub struct ColliderHierarchyPlugin;
impl Plugin for ColliderHierarchyPlugin {
fn build(&self, app: &mut App) {
app.add_observer(
|trigger: On<Add, (RigidBody, ColliderMarker)>,
mut commands: Commands,
query: Query<(), With<ColliderMarker>>,
rb_query: Query<Entity, With<RigidBody>>,
parent_query: Query<&ChildOf>| {
let entity = trigger.entity;
if !query.contains(entity) {
return;
}
if let Some(body) = rb_query.get(entity).ok().map_or_else(
|| {
parent_query
.iter_ancestors(trigger.entity)
.find(|&entity| rb_query.contains(entity))
},
Some,
) {
commands.entity(entity).insert(ColliderOf { body });
}
},
);
app.add_observer(
|trigger: On<Remove, (RigidBody, ColliderMarker)>,
mut commands: Commands,
query: Query<(), With<ColliderMarker>>| {
let entity = trigger.entity;
if query.contains(entity) {
commands.entity(entity).try_remove::<ColliderOf>();
}
},
);
app.add_observer(on_collider_body_changed);
app.add_observer(on_body_removed);
}
}
fn on_collider_body_changed(
trigger: On<Insert, (ChildOf, RigidBody, AncestorMarker<ColliderMarker>)>,
mut commands: Commands,
query: Query<
Has<ColliderMarker>,
Or<(With<ColliderMarker>, With<AncestorMarker<ColliderMarker>>)>,
>,
child_query: Query<&Children>,
parent_query: Query<&ChildOf>,
body_query: Query<Entity, With<RigidBody>>,
collider_query: Query<(), With<ColliderMarker>>,
) {
let entity = trigger.entity;
let Ok(is_collider) = query.get(entity) else {
return;
};
let Some(body) = body_query.get(entity).ok().map_or_else(
|| {
parent_query
.iter_ancestors(trigger.entity)
.find(|&entity| body_query.contains(entity))
},
Some,
) else {
return;
};
if is_collider {
commands.entity(entity).insert(ColliderOf { body });
}
for child in child_query.iter_descendants(entity) {
if body_query.contains(child) {
break;
}
if collider_query.contains(child) {
commands.entity(child).insert(ColliderOf { body });
}
}
}
fn on_body_removed(
trigger: On<Remove, RigidBody>,
mut commands: Commands,
body_collider_query: Query<&RigidBodyColliders>,
) {
let body = trigger.entity;
if let Ok(colliders) = body_collider_query.get(body) {
for collider in colliders.iter() {
commands
.entity(collider)
.try_remove::<(ColliderOf, ColliderTransform)>();
}
}
}