use super::Node; use bevy_ecs::{Entity, Query, With, Without}; use bevy_transform::prelude::{Children, Parent, Transform}; pub const UI_Z_STEP: f32 = 0.001; pub fn ui_z_system( root_node_query: Query, Without)>, mut node_query: Query<&mut Transform, With>, children_query: Query<&Children>, ) { let mut current_global_z = 0.0; for entity in root_node_query.iter() { current_global_z = update_hierarchy( &children_query, &mut node_query, entity, current_global_z, current_global_z, ); } } fn update_hierarchy( children_query: &Query<&Children>, node_query: &mut Query<&mut Transform, With>, entity: Entity, parent_global_z: f32, mut current_global_z: f32, ) -> f32 { current_global_z += UI_Z_STEP; if let Ok(mut transform) = node_query.get_mut(entity) { transform.translation.z = current_global_z - parent_global_z; } if let Ok(children) = children_query.get(entity) { let current_parent_global_z = current_global_z; for child in children.iter().cloned() { current_global_z = update_hierarchy( children_query, node_query, child, current_parent_global_z, current_global_z, ); } } current_global_z } #[cfg(test)] mod tests { use bevy_ecs::{Commands, IntoSystem, Resources, Schedule, World}; use bevy_transform::{components::Transform, hierarchy::BuildChildren}; use crate::Node; use super::{ui_z_system, UI_Z_STEP}; fn node_with_transform(name: &str) -> (String, Node, Transform) { (name.to_owned(), Node::default(), Transform::default()) } fn node_without_transform(name: &str) -> (String, Node) { (name.to_owned(), Node::default()) } fn get_steps(transform: &Transform) -> u32 { (transform.translation.z / UI_Z_STEP).round() as u32 } #[test] fn test_ui_z_system() { let mut world = World::default(); let mut resources = Resources::default(); let mut commands = Commands::default(); commands.set_entity_reserver(world.get_entity_reserver()); commands.spawn(node_with_transform("0")); commands .spawn(node_with_transform("1")) .with_children(|parent| { parent .spawn(node_with_transform("1-0")) .with_children(|parent| { parent.spawn(node_with_transform("1-0-0")); parent.spawn(node_without_transform("1-0-1")); parent.spawn(node_with_transform("1-0-2")); }); parent.spawn(node_with_transform("1-1")); parent .spawn(node_without_transform("1-2")) .with_children(|parent| { parent.spawn(node_with_transform("1-2-0")); parent.spawn(node_with_transform("1-2-1")); parent .spawn(node_with_transform("1-2-2")) .with_children(|_| ()); parent.spawn(node_with_transform("1-2-3")); }); parent.spawn(node_with_transform("1-3")); }); commands .spawn(node_without_transform("2")) .with_children(|parent| { parent .spawn(node_with_transform("2-0")) .with_children(|_parent| ()); parent .spawn(node_with_transform("2-1")) .with_children(|parent| { parent.spawn(node_with_transform("2-1-0")); }); }); commands.apply(&mut world, &mut resources); let mut schedule = Schedule::default(); schedule.add_stage("update"); schedule.add_system_to_stage("update", ui_z_system.system()); schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); let mut actual_result = world .query::<(&String, &Transform)>() .map(|(name, transform)| (name.clone(), get_steps(transform))) .collect::>(); actual_result.sort_unstable_by_key(|(name, _)| name.clone()); let expected_result = vec![ ("0".to_owned(), 1), ("1".to_owned(), 1), ("1-0".to_owned(), 1), ("1-0-0".to_owned(), 1), // 1-0-1 has no transform ("1-0-2".to_owned(), 3), ("1-1".to_owned(), 5), // 1-2 has no transform ("1-2-0".to_owned(), 1), ("1-2-1".to_owned(), 2), ("1-2-2".to_owned(), 3), ("1-2-3".to_owned(), 4), ("1-3".to_owned(), 11), // 2 has no transform ("2-0".to_owned(), 1), ("2-1".to_owned(), 2), ("2-1-0".to_owned(), 1), ]; assert_eq!(actual_result, expected_result); } }