Add a trait for commands that run for a given Entity (#7015)
				
					
				
			# Objective Resolve #6156. The most common type of command is one that runs for a single entity. Built-in commands like this can be ergonomically added to the command queue using the `EntityCommands` struct. However, adding custom entity commands to the queue is quite cumbersome. You must first spawn an entity, store its ID in a local, then construct a command using that ID and add it to the queue. This prevents method chaining, which is the main benefit of using `EntityCommands`. ### Example (before) ```rust struct MyCustomCommand(Entity); impl Command for MyCustomCommand { ... } let id = commands.spawn((...)).id(); commmands.add(MyCustomCommand(id)); ``` ## Solution Add the `EntityCommand` trait, which allows directly adding per-entity commands to the `EntityCommands` struct. ### Example (after) ```rust struct MyCustomCommand; impl EntityCommand for MyCustomCommand { ... } commands.spawn((...)).add(MyCustomCommand); ``` --- ## Changelog - Added the trait `EntityCommand`. This is a counterpart of `Command` for types that execute code for a single entity. ## Future Work If we feel its necessary, we can simplify built-in commands (such as `Despawn`) to use this trait.
This commit is contained in:
		
							parent
							
								
									83b602a77c
								
							
						
					
					
						commit
						65d390163f
					
				| @ -528,6 +528,85 @@ impl<'w, 's> Commands<'w, 's> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// A [`Command`] which gets executed for a given [`Entity`].
 | ||||||
|  | ///
 | ||||||
|  | /// # Examples
 | ||||||
|  | ///
 | ||||||
|  | /// ```
 | ||||||
|  | /// # use std::collections::HashSet;
 | ||||||
|  | /// # use bevy_ecs::prelude::*;
 | ||||||
|  | /// 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::<Counter>();
 | ||||||
|  | ///         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::<Counter>();
 | ||||||
|  | /// #
 | ||||||
|  | /// # 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`].
 | ||||||
|  |     fn with_entity(self, id: Entity) -> WithEntity<Self> | ||||||
|  |     where | ||||||
|  |         Self: Sized, | ||||||
|  |     { | ||||||
|  |         WithEntity { cmd: self, id } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Turns an [`EntityCommand`] type into a [`Command`] type.
 | ||||||
|  | pub struct WithEntity<C: EntityCommand> { | ||||||
|  |     cmd: C, | ||||||
|  |     id: Entity, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<C: EntityCommand> Command for WithEntity<C> { | ||||||
|  |     #[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).
 | /// A list of commands that will be run to modify an [entity](crate::entity).
 | ||||||
| pub struct EntityCommands<'w, 's, 'a> { | pub struct EntityCommands<'w, 's, 'a> { | ||||||
|     entity: Entity, |     entity: Entity, | ||||||
| @ -690,6 +769,27 @@ 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<C: EntityCommand>(&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.
 |     /// Logs the components of the entity at the info level.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// # Panics
 |     /// # Panics
 | ||||||
| @ -716,6 +816,15 @@ where | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl<F> EntityCommand for F | ||||||
|  | where | ||||||
|  |     F: FnOnce(Entity, &mut World) + Send + 'static, | ||||||
|  | { | ||||||
|  |     fn write(self, id: Entity, world: &mut World) { | ||||||
|  |         self(id, world); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Spawn<T> { | pub struct Spawn<T> { | ||||||
|     pub bundle: T, |     pub bundle: T, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 JoJoJet
						JoJoJet