use bevy_ecs::prelude::*;
use bevy_ecs::query::Added;
use bevy_ecs::schedule::ScheduleLabel;
use crate::App;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)]
pub struct Parent(pub Entity);
#[derive(Debug, Clone, Component)]
pub struct Children(pub smallvec::SmallVec<[Entity; 4]>);
impl Children {
#[must_use]
pub fn new() -> Self {
Self(smallvec::SmallVec::new())
}
#[must_use]
pub fn contains(&self, entity: Entity) -> bool {
self.0.contains(&entity)
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Entity> {
self.0.iter()
}
}
impl Default for Children {
fn default() -> Self {
Self::new()
}
}
pub fn sync_children(world: &mut World) {
let pairs: Vec<(Entity, Entity)> = world
.query_filtered::<(Entity, &Parent), Added<Parent>>()
.iter(world)
.map(|(child, parent)| (child, parent.0))
.collect();
if pairs.is_empty() {
return;
}
for (child_entity, parent_entity) in pairs {
if world.get::<Children>(parent_entity).is_none() {
world.entity_mut(parent_entity).insert(Children::new());
}
let already_present = world
.get::<Children>(parent_entity)
.is_some_and(|c| c.contains(child_entity));
if !already_present && let Some(mut children) = world.get_mut::<Children>(parent_entity) {
children.0.push(child_entity);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ScheduleLabel)]
pub struct PostUpdate;
pub struct HierarchyPlugin;
impl crate::GamePlugin for HierarchyPlugin {
fn name(&self) -> &'static str {
"hierarchy"
}
fn build(&mut self, app: &mut App) {
app.add_system(sync_children);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn children_new_is_empty() {
let children = Children::new();
assert!(children.is_empty());
assert_eq!(children.len(), 0);
}
#[test]
fn children_contains() {
let entity = Entity::from_bits(1);
let children = Children(smallvec::SmallVec::from_slice(&[entity]));
assert!(children.contains(entity));
assert!(!children.contains(Entity::from_bits(2)));
}
#[test]
fn sync_children_writes_immediately() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child = world.spawn(Parent(parent)).id();
sync_children(&mut world);
let children = world
.get::<Children>(parent)
.expect("parent should have Children");
assert!(
children.contains(child),
"child should be in Children after sync"
);
}
}