mirror of
https://github.com/bevyengine/bevy
synced 2024-11-23 05:03:47 +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).
|
/// 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 a new issue