mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
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
1 changed files with 109 additions and 0 deletions
|
@ -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).
|
||||
pub struct EntityCommands<'w, 's, 'a> {
|
||||
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.
|
||||
///
|
||||
/// # 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)]
|
||||
pub struct Spawn<T> {
|
||||
pub bundle: T,
|
||||
|
|
Loading…
Reference in a new issue