use crate::components::*;
use bevy_ecs::{Commands, Entity, IntoQuerySystem, Query, System, Without};
use smallvec::SmallVec;
use std::collections::HashMap;
pub fn missing_previous_parent_system(
mut commands: Commands,
mut query: Query<Without<PreviousParent, (Entity, &Parent)>>,
) {
for (entity, _parent) in &mut query.iter() {
log::trace!("Adding missing PreviousParent to {:?}", entity);
commands.insert_one(entity, PreviousParent(None));
}
}
pub fn parent_update_system(
mut commands: Commands,
mut removed_parent_query: Query<Without<Parent, (Entity, &PreviousParent)>>,
mut changed_parent_query: Query<(Entity, &Parent, &mut PreviousParent)>,
children_query: Query<&mut Children>,
) {
for (entity, previous_parent) in &mut removed_parent_query.iter() {
log::trace!("Parent was removed from {:?}", entity);
if let Some(previous_parent_entity) = previous_parent.0 {
if let Ok(mut previous_parent_children) =
children_query.get_mut::<Children>(previous_parent_entity)
{
log::trace!(" > Removing {:?} from it's prev parent's children", entity);
previous_parent_children.0.retain(|e| *e != entity);
}
}
}
let mut children_additions = HashMap::<Entity, SmallVec<[Entity; 8]>>::new();
for (entity, parent, mut previous_parent) in &mut changed_parent_query.iter() {
log::trace!("Parent changed for {:?}", entity);
if let Some(previous_parent_entity) = previous_parent.0 {
if previous_parent_entity == parent.0 {
log::trace!(" > But the previous parent is the same, ignoring...");
continue;
}
if let Ok(mut previous_parent_children) =
children_query.get_mut::<Children>(previous_parent_entity)
{
log::trace!(" > Removing {:?} from prev parent's children", entity);
(*previous_parent_children).0.retain(|e| *e != entity);
}
}
*previous_parent = PreviousParent(Some(parent.0));
log::trace!("Adding {:?} to it's new parent {:?}", entity, parent.0);
if let Ok(mut new_parent_children) = children_query.get_mut::<Children>(parent.0) {
log::trace!(
" > The new parent {:?} already has a `Children`, adding to it.",
parent.0
);
(*new_parent_children).0.push(entity);
} else {
log::trace!(
"The new parent {:?} doesn't yet have `Children` component.",
parent.0
);
children_additions
.entry(parent.0)
.or_insert_with(Default::default)
.push(entity);
}
}
children_additions.iter().for_each(|(k, v)| {
log::trace!(
"Flushing: Entity {:?} adding `Children` component {:?}",
k,
v
);
commands.insert_one(*k, Children::with(v));
});
}
pub fn hierarchy_maintenance_systems() -> Vec<Box<dyn System>> {
vec![
missing_previous_parent_system.system(),
parent_update_system.system(),
]
}
#[cfg(test)]
mod test {
use super::*;
use crate::{hierarchy::BuildChildren, transform_systems};
use bevy_ecs::{Resources, Schedule, World};
#[test]
fn correct_children() {
let mut world = World::default();
let mut resources = Resources::default();
let mut schedule = Schedule::default();
schedule.add_stage("update");
for system in transform_systems() {
schedule.add_system_to_stage("update", system);
}
let mut commands = Commands::default();
let mut parent = None;
let mut children = Vec::new();
commands
.spawn((Translation::new(1.0, 0.0, 0.0), Transform::identity()))
.for_current_entity(|entity| parent = Some(entity))
.with_children(|parent| {
parent
.spawn((Translation::new(0.0, 2.0, 0.0), Transform::identity()))
.for_current_entity(|entity| children.push(entity))
.spawn((Translation::new(0.0, 0.0, 3.0), Transform::identity()))
.for_current_entity(|entity| children.push(entity));
});
let parent = parent.unwrap();
commands.apply(&mut world, &mut resources);
schedule.run(&mut world, &mut resources);
assert_eq!(
world
.get::<Children>(parent)
.unwrap()
.0
.iter()
.cloned()
.collect::<Vec<_>>(),
children,
);
(*world.get_mut::<Parent>(children[0]).unwrap()).0 = children[1];
schedule.run(&mut world, &mut resources);
assert_eq!(
world
.get::<Children>(parent)
.unwrap()
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![children[1]]
);
assert_eq!(
world
.get::<Children>(children[1])
.unwrap()
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![children[0]]
);
world.despawn(children[0]).unwrap();
schedule.run(&mut world, &mut resources);
assert_eq!(
world
.get::<Children>(parent)
.unwrap()
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![children[1]]
);
}
}