Description
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.