From c0ded870a9ddeaeb37d1d52d802b00b63e0c157f Mon Sep 17 00:00:00 2001 From: JoJoJet <21144246+JoJoJet@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:46:52 -0500 Subject: [PATCH 1/5] add the `EntityCommand` trait --- crates/bevy_ecs/src/system/commands/mod.rs | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index e89d3659486c8..c36041fc9ed80 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -528,6 +528,30 @@ impl<'w, 's> Commands<'w, 's> { } } +/// A [`Command`] which gets executed for a given [`Entity`]. +pub trait EntityCommand: Send + 'static { + fn write(self, id: Entity, world: &mut World); + /// Returns a [`Command`] which executes this [`EntityCommand`] for the given [`Entity`]. + fn with_entity(self, id: Entity) -> WithEntity + where + Self: Sized, + { + WithEntity { cmd: self, id } + } +} + +pub struct WithEntity { + cmd: C, + id: Entity, +} + +impl Command for WithEntity { + #[inline] + fn write(self, world: &mut World) { + self.cmd.write(self.id, world); + } +} + /// A list of commands that will be run to modify an [entity](crate::entity). pub struct EntityCommands<'w, 's, 'a> { entity: Entity, @@ -690,6 +714,12 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { }); } + /// Pushes an [`EntityCommand`] to the queue, which will get executed for the current [`Entity`]. + pub fn add(&mut self, command: C) -> &mut Self { + self.commands.add(command.with_entity(self.entity)); + self + } + /// Logs the components of the entity at the info level. /// /// # Panics From 2a6b5a6465fcf2b7c685e5c6da12554a1b5090f2 Mon Sep 17 00:00:00 2001 From: JoJoJet <21144246+JoJoJet@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:35:32 -0500 Subject: [PATCH 2/5] add an example to `EntityCommand` --- crates/bevy_ecs/src/system/commands/mod.rs | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index c36041fc9ed80..7bc123ac76e3c 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -529,6 +529,60 @@ impl<'w, 's> Commands<'w, 's> { } /// A [`Command`] which gets executed for a given [`Entity`]. +/// +/// # Examples +/// +/// ``` +/// # use std::collections::HashSet; +/// # use bevy_ecs::{prelude::*, self as bevy_ecs}; +/// use bevy_ecs::system::EntityCommand; +/// # +/// # #[derive(Component, PartialEq)] +/// # struct Name(String); +/// # impl Name { +/// # fn new(s: String) -> Self { Name(s) } +/// # fn as_str(&self) -> &str { &self.0 } +/// # } +/// +/// #[derive(Resource, Default)] +/// struct Counter(i64); +/// +/// /// A `Command` which names an entity based on a global counter. +/// struct CountName; +/// +/// impl EntityCommand for CountName { +/// fn write(self, id: Entity, world: &mut World) { +/// // Get the current value of the counter, and increment it for next time. +/// let mut counter = world.resource_mut::(); +/// let i = counter.0; +/// counter.0 += 1; +/// +/// // Name the entity after the value of the counter. +/// world.entity_mut(id).insert(Name::new(format!("Entity #{i}"))); +/// } +/// } +/// +/// // App creation boilerplate omitted... +/// # let mut world = World::new(); +/// # world.init_resource::(); +/// # +/// # let mut setup_stage = SystemStage::single_threaded().with_system(setup); +/// # let mut assert_stage = SystemStage::single_threaded().with_system(assert_names); +/// # +/// # setup_stage.run(&mut world); +/// # assert_stage.run(&mut world); +/// +/// fn setup(mut commands: Commands) { +/// commands.spawn_empty().add(CountName); +/// commands.spawn_empty().add(CountName); +/// } +/// +/// fn assert_names(named: Query<&Name>) { +/// // We use a HashSet because we do not care about the order. +/// let names: HashSet<_> = named.iter().map(Name::as_str).collect(); +/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"])); +/// } +/// ``` pub trait EntityCommand: Send + 'static { fn write(self, id: Entity, world: &mut World); /// Returns a [`Command`] which executes this [`EntityCommand`] for the given [`Entity`]. From 3487d35ca2ecd4778bd308f8d99d21bc69e44903 Mon Sep 17 00:00:00 2001 From: JoJoJet <21144246+JoJoJet@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:37:48 -0500 Subject: [PATCH 3/5] add anonymous entity commands --- crates/bevy_ecs/src/system/commands/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 7bc123ac76e3c..38a8cd5e6a163 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -800,6 +800,15 @@ where } } +impl EntityCommand for F +where + F: FnOnce(Entity, &mut World) + Send + 'static, +{ + fn write(self, id: Entity, world: &mut World) { + self(id, world); + } +} + #[derive(Debug)] pub struct Spawn { pub bundle: T, From cdb94dd1dfacb03a598c39b36f9ed3eafa657b9d Mon Sep 17 00:00:00 2001 From: JoJoJet <21144246+JoJoJet@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:49:46 -0500 Subject: [PATCH 4/5] add an example to `EntityCommands::add` --- crates/bevy_ecs/src/system/commands/mod.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 38a8cd5e6a163..6f248c4d891fe 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -534,7 +534,7 @@ impl<'w, 's> Commands<'w, 's> { /// /// ``` /// # use std::collections::HashSet; -/// # use bevy_ecs::{prelude::*, self as bevy_ecs}; +/// # use bevy_ecs::prelude::*; /// use bevy_ecs::system::EntityCommand; /// # /// # #[derive(Component, PartialEq)] @@ -769,6 +769,21 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { } /// Pushes an [`EntityCommand`] to the queue, which will get executed for the current [`Entity`]. + /// + /// # Examples + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # fn my_system(mut commands: Commands) { + /// commands + /// .spawn_empty() + /// // Closures with this signature implement `EntityCommand`. + /// .add(|id: Entity, world: &mut World| { + /// println!("Executed an EntityCommand for {id:?}"); + /// }); + /// # } + /// # bevy_ecs::system::assert_is_system(my_system); + /// ``` pub fn add(&mut self, command: C) -> &mut Self { self.commands.add(command.with_entity(self.entity)); self From 7d8c5a041b2fe8e1a076f9c015d08680d546ab49 Mon Sep 17 00:00:00 2001 From: JoJoJet <21144246+JoJoJet@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:06:15 -0500 Subject: [PATCH 5/5] add docs to `WithEntity` --- crates/bevy_ecs/src/system/commands/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 6f248c4d891fe..da4f78819f6a0 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -594,6 +594,7 @@ pub trait EntityCommand: Send + 'static { } } +/// Turns an [`EntityCommand`] type into a [`Command`] type. pub struct WithEntity { cmd: C, id: Entity,