Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commands that run for a given entity #6156

Closed
JoJoJet opened this issue Oct 3, 2022 · 1 comment
Closed

Commands that run for a given entity #6156

JoJoJet opened this issue Oct 3, 2022 · 1 comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use

Comments

@JoJoJet
Copy link
Member

JoJoJet commented Oct 3, 2022

What problem does this solve or what need does it fill?

In my crate bevy_mouse_tracking, users need to be able to insert components in a fully-initialized state. This initialization requires access to the world, which means the most natural way to express this is through commands. Currently, this would look like

// Library-side
pub struct InsertInitialized(pub Entity);
impl Command for InsertInitialized {
    fn write(self, world: &mut World) {
        let initialized = perform_initialization(self.0, world);
        world.entity(self.0).insert(initialized);
    }
}

// User-side
fn my_system(mut commands: Commands) {
    let my_entity = commands.spawn_empty().id();
    commands.add(InsertInitialized(my_entity));
}

This is very cumbersome on the user side.

What solution would you like?

Add a trait for commands that run for a given entity.

pub trait EntityCommand: Send + 'static {
    fn write_for(self, _: Entity, _: &mut World);
    fn with_entity(self, _: Entity) -> impl Command { ... }
}

The previous example now becomes:

// Library-side
pub struct InsertInitialized;
impl EntityCommand for InsertInitialized {
    fn write_for(self, entity: Entity, world: &mut World) {
        let initialized = perform_initialization(entity, world);
        world.entity(entity).insert(initialized);
    }
}

// User-side
fn my_system(mut commands: Commands) {
    commands.spawn_empty().add_command(InsertInitialized);
}

What alternative(s) have you considered?

My current workaround is to add extension methods to EntityCommands (the struct, not this proposed trait) for each custom command. This is only slightly less annoying for users, as it requires them to bring an extension trait into scope. I don't think this is practical in the general case.

Additional context

This could also be used to implement built-in commands, such as GetOrSpawn, Insert, Despawn, etc. This would make direct use of these types more ergonomic.

@JoJoJet JoJoJet added C-Feature A new feature, making something new possible S-Needs-Triage This issue needs to be labelled labels Oct 3, 2022
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use and removed S-Needs-Triage This issue needs to be labelled C-Feature A new feature, making something new possible labels Oct 4, 2022
@jkb0o
Copy link
Contributor

jkb0o commented Nov 29, 2022

I've met the exactly same problem. I have some structs (kinda bundles) witch can be

  • added to the root, and it easy to do: commands.add(CoolThing::load("blahblh"))
  • or it could be added as child to exact node, and there is no out-of-the-box-solution, I'd like to do smth like this: commands.spawn_empty().add(CoolThing::load("blahblah"))

From my point of view Commands has spawn method as well as EntityCommands has it, but there is no add method for EntityCommands.

My workaround is to define own trait EntityCommandAdd and implement it for EntityCommand and it is ok unless enduser meet the name conflict.

It would be awesome if Bevy provide the trait

pub trait EntityCommand:  Send + 'static {
  fn write(self, entity: Entity, world: &mut World);
}

and add method for EntityCommands

impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
    // ...
    pub fn add<C: EntityCommand>(&mut self, command: C) {
        // ...
    }
}

bors bot pushed a commit that referenced this issue Dec 25, 2022
# 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.
@bors bors bot closed this as completed in 65d3901 Dec 25, 2022
alradish pushed a commit to alradish/bevy that referenced this issue Jan 22, 2023
# Objective

Resolve bevyengine#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.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this issue Feb 1, 2023
# Objective

Resolve bevyengine#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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants