Custom commands in Bevy with extension traits

Bevy is an ECS-based game engine built in Rust. Extension traits are a pattern in rust that allows you to add methods to an existing type defined outside of your crate. You can probably guess where I’m going with this.

In bevy, any system can access the Commands structure to issue commands manipulate the World. The most common one would probably be the Commands#spawn method which lets you spawn an entity with the components you specify. You can pass a structure implementing the Bundle trait to this method. Luckily, tuples of none to many components implement this trait thanks to macro magic, so you can just call the method like:

commands.spawn((Component1 {x: 3.0, y: 4.0}, Component2 {value: true}))

Or, you can easily define your own bundles and use them:

use bevy::ecs::*;

#[derive(Bundle)]
struct HumbleBundle {
    component1: Component1,
    component2: Component2,
}

// ... lines later, somewhere in a system
commands.spawn(HumbleBundle {
    component1: Component1 {x: 3.0, y: 4.0},
    component2: Component2 {value: true}
});

But maybe you need to spawn multiple entities that refer to eachother or something. Then, you need to implement a new Command yourself.

use bevy::ecs::*;

struct ReferringComponent {
    refers_to: Entity
}

struct ComponentFoo {
    bar: i32
}

struct SpawnReferringPair {
    first_bar: i32,
    second_bar: i32,
}

// create two entities, the second one referring to the first one
impl Command for SpawnReferringPair {
    fn write(self: Box<Self>, world: &mut World, resources: &mut Resources) {
        let first_entity = world.spawn((ComponentFoo {bar: self.first_bar}, ));
        world.spawn((ComponentFoo {bar: self.second_bar}, ReferringComponent {refers_to: first_entity}));
    }
}

And we can use this Command with Commands, like:

commands.add_command(SpawnReferringPair {first_bar: 5, second_bar: 10});

Now, this is completely fine and functional. But I think we can make it prettier, so we can use it like:

commands.spawn_referring_pair(5, 10);

We just have to add a method to Bevy’s already defined Commands structure with an extension trait.

// imagine that the code defining SpawnReferringPair is here as well :)

trait CommandsExt {
    fn spawn_referring_pair(&mut self, first_bar: i32, second_bar: i32) -> &mut Self;
}

impl CommandsExt for Commands {
    fn spawn_referring_pair(&mut self, first_bar: i32, second_bar: i32) -> &mut Self {
        self.add_command(SpawnReferringPair {
            first_bar, second_bar // field init shorthand
        });
        self
    }
}

And voila, we can use this method just like the spawn method:

commands
    .spawn_referring_pair(5, 10)
    .spawn((SomeOtherComponent,))
    .spawn_referring_pair(1024, 1);

In conclusion, don’t let the computer tell you what to do, make it do what you want, however arbitrary it might be. Also, Bevy is pre-1.0 and as much as they try to keep things backwards-compatible, this article might not be correct beyond 0.4, or it could be, check the source code, nerd.