From d0afdc6b45316744caefb8b3e9ee335c02ff56fa Mon Sep 17 00:00:00 2001 From: JaySpruce Date: Fri, 6 Dec 2024 09:54:35 -0600 Subject: [PATCH] Move `clone_entity` commands to `EntityCommands` (#16672) ## Objective I was resolving a conflict between #16132 and my PR #15929 and thought the `clone_entity` commands made more sense in `EntityCommands`. ## Solution Moved `Commands::clone_entity` to `EntityCommands::clone`, moved `Commands::clone_entity_with` to `EntityCommands::clone_with`. ## Testing Ran the two tests that used the old methods. ## Showcase ``` // Create a new entity and keep its EntityCommands. let mut entity = commands.spawn((ComponentA(10), ComponentB(20))); // Create a clone of the first entity let mut entity_clone = entity.clone(); ``` The only potential downside is that the method name is now the same as the one from the `Clone` trait. `EntityCommands` doesn't implement `Clone` though, so there's no actual conflict. Maybe I'm biased because this'll work better with my PR, but I think the UX is nicer regardless. --- crates/bevy_ecs/src/system/commands/mod.rs | 141 ++++++++++++--------- crates/bevy_hierarchy/src/hierarchy.rs | 6 +- 2 files changed, 82 insertions(+), 65 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 6102278de2..0879b8520f 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -272,69 +272,6 @@ impl<'w, 's> Commands<'w, 's> { } } - /// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`], returning [`EntityCommands`] of the cloned entity. - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// - /// #[derive(Component, Clone)] - /// struct ComponentA(u32); - /// #[derive(Component, Clone)] - /// struct ComponentB(u32); - /// - /// fn example_system(mut commands: Commands) { - /// // Create a new entity and retrieve its id. - /// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id(); - /// - /// // Create a clone of the first entity, but without ComponentB - /// let entity_clone = commands.clone_entity_with(entity, |builder| { - /// builder.deny::(); - /// }).id(); - /// } - /// # bevy_ecs::system::assert_is_system(example_system); - pub fn clone_entity_with( - &mut self, - entity: Entity, - f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, - ) -> EntityCommands<'_> { - let cloned_entity = self.spawn_empty().id(); - self.queue(move |world: &mut World| { - let mut builder = EntityCloneBuilder::new(world); - f(&mut builder); - builder.clone_entity(entity, cloned_entity); - }); - EntityCommands { - commands: self.reborrow(), - entity: cloned_entity, - } - } - - /// Clones an entity and returns [`EntityCommands`] of the cloned entity. - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// - /// #[derive(Component, Clone)] - /// struct ComponentA(u32); - /// #[derive(Component, Clone)] - /// struct ComponentB(u32); - /// - /// fn example_system(mut commands: Commands) { - /// // Create a new entity and retrieve its id. - /// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id(); - /// - /// // Create a clone of the first entity - /// let entity_clone = commands.clone_entity(entity).id(); - /// } - /// # bevy_ecs::system::assert_is_system(example_system); - pub fn clone_entity(&mut self, entity: Entity) -> EntityCommands<'_> { - self.clone_entity_with(entity, |_| {}) - } - /// Reserves a new empty [`Entity`] to be spawned, and returns its corresponding [`EntityCommands`]. /// /// See [`World::spawn_empty`] for more details. @@ -1780,6 +1717,73 @@ impl<'a> EntityCommands<'a> { ) -> &mut Self { self.queue(observe(system)) } + + /// Clones an entity and returns the [`EntityCommands`] of the clone. + /// + /// # Panics + /// + /// The command will panic when applied if the original entity does not exist. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Component, Clone)] + /// struct ComponentA(u32); + /// #[derive(Component, Clone)] + /// struct ComponentB(u32); + /// + /// fn example_system(mut commands: Commands) { + /// // Create a new entity and keep its EntityCommands. + /// let mut entity = commands.spawn((ComponentA(10), ComponentB(20))); + /// + /// // Create a clone of the first entity + /// let entity_clone = entity.clone(); + /// } + /// # bevy_ecs::system::assert_is_system(example_system); + pub fn clone(&mut self) -> EntityCommands<'_> { + self.clone_with(|_| {}) + } + + /// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`], + /// returning the [`EntityCommands`] of the clone. + /// + /// # Panics + /// + /// The command will panic when applied if the original entity does not exist. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Component, Clone)] + /// struct ComponentA(u32); + /// #[derive(Component, Clone)] + /// struct ComponentB(u32); + /// + /// fn example_system(mut commands: Commands) { + /// // Create a new entity and keep its EntityCommands. + /// let mut entity = commands.spawn((ComponentA(10), ComponentB(20))); + /// + /// // Create a clone of the first entity, but without ComponentB + /// let entity_clone = entity.clone_with(|builder| { + /// builder.deny::(); + /// }); + /// } + /// # bevy_ecs::system::assert_is_system(example_system); + pub fn clone_with( + &mut self, + f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + ) -> EntityCommands<'_> { + let entity_clone = self.commands().spawn_empty().id(); + self.queue(clone_entity_with(entity_clone, f)); + EntityCommands { + commands: self.commands_mut().reborrow(), + entity: entity_clone, + } + } } /// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type. @@ -2254,6 +2258,17 @@ fn observe( } } +fn clone_entity_with( + entity_clone: Entity, + f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, +) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + let mut builder = EntityCloneBuilder::new(world); + f(&mut builder); + builder.clone_entity(entity, entity_clone); + } +} + #[cfg(test)] #[allow(clippy::float_cmp, clippy::approx_constant)] mod tests { diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index d48e74d864..b3215bfb66 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -443,7 +443,8 @@ mod tests { }) .id(); let e_clone = commands - .clone_entity_with(e, |builder| { + .entity(e) + .clone_with(|builder| { builder.recursive(true); }) .id(); @@ -481,7 +482,8 @@ mod tests { let parent = commands.spawn_empty().add_child(child).id(); let child_clone = commands - .clone_entity_with(child, |builder| { + .entity(child) + .clone_with(|builder| { builder.as_child(true); }) .id();