diff --git a/Cargo.toml b/Cargo.toml index f95b76f75a9ee..788863fb9438e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -301,6 +301,10 @@ path = "examples/ecs/state.rs" name = "system_chaining" path = "examples/ecs/system_chaining.rs" +[[example]] +name = "system_command" +path = "examples/ecs/system_command.rs" + [[example]] name = "system_param" path = "examples/ecs/system_param.rs" diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 0488b09e4f770..a1f2b1a4e57cd 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -10,6 +10,8 @@ use bevy_utils::tracing::debug; pub use command_queue::CommandQueue; use std::marker::PhantomData; +use super::{IntoSystem, System}; + /// A [`World`] mutation. pub trait Command: Send + Sync + 'static { fn write(self, world: &mut World); @@ -152,6 +154,30 @@ impl<'a> Commands<'a> { }); } + /// Run a one-off [`System`]. + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # struct MyComponent; + /// + /// # fn my_system(_components: Query<&MyComponent>) {} + /// + /// # fn main_system(mut commands: Commands) { + /// // Can take a standard system function + /// commands.run_system(my_system); + /// // Can take a closure system + /// commands.run_system(|query: Query<&MyComponent>| { + /// println!("count: {}", query.iter().len()); + /// }); + /// # } + /// # main_system.system(); + /// ``` + pub fn run_system(&mut self, system: impl IntoSystem<(), (), Params>) { + self.queue.push(RunSystem { + system: Box::new(system.system()), + }); + } + /// Adds a command directly to the command list. pub fn add(&mut self, command: C) { self.queue.push(command); @@ -387,6 +413,18 @@ impl Command for RemoveResource { } } +pub struct RunSystem { + pub system: Box>, +} + +impl Command for RunSystem { + fn write(mut self, world: &mut World) { + self.system.initialize(world); + self.system.run((), world); + self.system.apply_buffers(world); + } +} + #[cfg(test)] #[allow(clippy::float_cmp, clippy::approx_constant)] mod tests { diff --git a/examples/README.md b/examples/README.md index 37b76a3a34830..24fe8aad7701d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -167,6 +167,7 @@ Example | File | Description `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) `state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state `system_chaining` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`) +`system_command` | [`ecs/system_command.rs`](./ecs/system_command.rs) | Run a system from a command `system_param` | [`ecs/system_param.rs`](./ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` `system_sets` | [`ecs/system_sets.rs`](./ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion `timers` | [`ecs/timers.rs`](./ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state diff --git a/examples/ecs/system_command.rs b/examples/ecs/system_command.rs new file mode 100644 index 0000000000000..1e539040e9c2b --- /dev/null +++ b/examples/ecs/system_command.rs @@ -0,0 +1,38 @@ +use bevy::{prelude::*, utils::Duration}; + +/// This example triggers a system from a command +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_startup_system(spawn) + .add_system(trigger_sync) + .run(); +} + +pub struct Player; + +/// Spawn some players to sync +fn spawn(mut commands: Commands) { + commands.spawn().insert(Player); + commands.spawn().insert(Player); +} + +fn trigger_sync(mut commands: Commands, mut last_sync: Local, time: Res