diff --git a/crates/bevy_transform/src/lib.rs b/crates/bevy_transform/src/lib.rs index c68f01b2e0b5b6..f6d1b4946dc86b 100644 --- a/crates/bevy_transform/src/lib.rs +++ b/crates/bevy_transform/src/lib.rs @@ -86,6 +86,7 @@ pub enum TransformSystem { pub fn transform_propagate_system_set() -> SystemSet { SystemSet::new() .with_system(systems::sync_simple_transforms) + .with_system(systems::sync_simple_transforms_orphaned) .with_system(systems::propagate_transforms) } @@ -103,6 +104,10 @@ impl Plugin for TransformPlugin { StartupStage::PostStartup, systems::sync_simple_transforms.label(TransformSystem::TransformPropagate), ) + .add_startup_system_to_stage( + StartupStage::PostStartup, + systems::sync_simple_transforms_orphaned.label(TransformSystem::TransformPropagate), + ) .add_startup_system_to_stage( StartupStage::PostStartup, systems::propagate_transforms.label(TransformSystem::TransformPropagate), @@ -111,6 +116,10 @@ impl Plugin for TransformPlugin { CoreStage::PostUpdate, systems::sync_simple_transforms.label(TransformSystem::TransformPropagate), ) + .add_system_to_stage( + CoreStage::PostUpdate, + systems::sync_simple_transforms_orphaned.label(TransformSystem::TransformPropagate), + ) .add_system_to_stage( CoreStage::PostUpdate, systems::propagate_transforms.label(TransformSystem::TransformPropagate), diff --git a/crates/bevy_transform/src/systems.rs b/crates/bevy_transform/src/systems.rs index 7c7545721f74e6..70979e844edb31 100644 --- a/crates/bevy_transform/src/systems.rs +++ b/crates/bevy_transform/src/systems.rs @@ -20,6 +20,21 @@ pub fn sync_simple_transforms( *global_transform = GlobalTransform::from(*transform); }); } +/// Update [`GlobalTransform`] component of entities that aren't in the hierarchy +/// and lost their parent last frame. +/// +/// Third party plugins should use [`transform_propagate_system_set`](crate::transform_propagate_system_set) +/// to propagate transforms correctly. +pub fn sync_simple_transforms_orphaned( + mut query: Query<(&Transform, &mut GlobalTransform), Without>, + changed: Query<(), Changed>, + orphaned: RemovedComponents, +) { + let mut iter = query.iter_many_mut(orphaned.iter().filter(|e| !changed.contains(*e))); + while let Some((transform, mut global_transform)) = iter.fetch_next() { + *global_transform = GlobalTransform::from(*transform); + } +} /// Update [`GlobalTransform`] component of entities based on entity hierarchy and /// [`Transform`] component. @@ -181,6 +196,10 @@ mod test { fn correct_parent_removed() { ComputeTaskPool::init(TaskPool::default); let mut world = World::default(); + let offset_global_transform = + |offset| GlobalTransform::from(Transform::from_xyz(offset, offset, offset)); + let offset_transform = + |offset| TransformBundle::from_transform(Transform::from_xyz(offset, offset, offset)); let mut update_stage = SystemStage::parallel(); update_stage.add_system_set(transform_propagate_system_set()); @@ -190,13 +209,9 @@ mod test { let mut command_queue = CommandQueue::default(); let mut commands = Commands::new(&mut command_queue, &world); - let root = commands - .spawn(TransformBundle::from_transform(Transform::from_xyz( - 9.9, 9.9, 9.9, - ))) - .id(); - let parent = commands.spawn(TransformBundle::IDENTITY).id(); - let child = commands.spawn(TransformBundle::IDENTITY).id(); + let root = commands.spawn(offset_transform(3.3)).id(); + let parent = commands.spawn(offset_transform(4.4)).id(); + let child = commands.spawn(offset_transform(5.5)).id(); commands.entity(parent).set_parent(root); commands.entity(child).set_parent(parent); command_queue.apply(&mut world); @@ -216,7 +231,19 @@ mod test { assert_eq!( world.get::(parent).unwrap(), - &GlobalTransform::from(Transform::IDENTITY) + &offset_global_transform(4.4) + ); + + // Remove parent of `child` + let mut command_queue = CommandQueue::default(); + let mut commands = Commands::new(&mut command_queue, &world); + commands.entity(child).remove_parent(); + command_queue.apply(&mut world); + schedule.run(&mut world); + + assert_eq!( + world.get::(child).unwrap(), + &offset_global_transform(5.5) ); }