From b5b18cc9c271fc9abcd5c134cbcd8fa29e4b67a1 Mon Sep 17 00:00:00 2001 From: Liam Gallagher Date: Mon, 13 Feb 2023 19:48:10 +1300 Subject: [PATCH 1/6] Basic run condition example --- Cargo.toml | 10 ++++++++ examples/README.md | 1 + examples/ecs/run_conditions.rs | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 examples/ecs/run_conditions.rs diff --git a/Cargo.toml b/Cargo.toml index 266831b4af5f2..f1ffe8afc5c48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -929,6 +929,16 @@ description = "Query for entities that had a specific component removed earlier category = "ECS (Entity Component System)" wasm = false +[[example]] +name = "run_conditions" +path = "examples/ecs/run_conditions.rs" + +[package.metadata.example.run_conditions] +name = "Run Conditions" +description = "Run systems only when one or multiple conditions are met" +category = "ECS (Entity Component System)" +wasm = false + [[example]] name = "startup_system" path = "examples/ecs/startup_system.rs" diff --git a/examples/README.md b/examples/README.md index cfa05e8b44756..f335db7f9a37d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -205,6 +205,7 @@ Example | Description [Nondeterministic System Order](../examples/ecs/nondeterministic_system_order.rs) | Systems run in paralell, but their order isn't always deteriministic. Here's how to detect and fix this. [Parallel Query](../examples/ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` [Removal Detection](../examples/ecs/removal_detection.rs) | Query for entities that had a specific component removed earlier in the current frame +[Run Conditions](../examples/ecs/run_conditions.rs) | Run systems only when one or multiple conditions are met [Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) [State](../examples/ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state [System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state diff --git a/examples/ecs/run_conditions.rs b/examples/ecs/run_conditions.rs new file mode 100644 index 0000000000000..8420f93bf69d4 --- /dev/null +++ b/examples/ecs/run_conditions.rs @@ -0,0 +1,42 @@ +//! This example demonstrates how to use run criterias to control when systems run. + +use bevy::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .init_resource::() + .add_system( + increment_input_counter + .run_if(resource_exists::()) + .run_if(is_input), + ) + .add_system( + print_input_counter + .run_if(resource_exists::()) + .run_if(|c: Res| c.is_changed() && !c.is_added()), + ) + .run(); +} + +#[derive(Resource, Default)] +struct InputCounter(usize); + +// Return true if the user has clicked, tapped or pressed the space bar +fn is_input( + keyboard_input: Res>, + mouse_button_input: Res>, + touch_input: Res, +) -> bool { + keyboard_input.just_pressed(KeyCode::Space) + || mouse_button_input.just_pressed(MouseButton::Left) + || touch_input.any_just_pressed() +} + +fn increment_input_counter(mut counter: ResMut) { + counter.0 += 1; +} + +fn print_input_counter(counter: Res) { + println!("Input counter: {}", counter.0); +} From 5e80e358f1c96f32b7caef732817f1775b072b6b Mon Sep 17 00:00:00 2001 From: Liam Gallagher Date: Tue, 14 Feb 2023 10:51:36 +1300 Subject: [PATCH 2/6] Demonstrate local parameters and lots more comments --- examples/ecs/run_conditions.rs | 72 ++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/examples/ecs/run_conditions.rs b/examples/ecs/run_conditions.rs index 8420f93bf69d4..9fffe926c2228 100644 --- a/examples/ecs/run_conditions.rs +++ b/examples/ecs/run_conditions.rs @@ -1,20 +1,49 @@ -//! This example demonstrates how to use run criterias to control when systems run. +//! This example demonstrates how to use run conditions to control when systems run. use bevy::prelude::*; fn main() { + println!(); + println!("For the first 2 seconds you will not be able to increment the counter"); + println!("Once that time has passed you can press space, enter, left mouse, right mouse or touch the screen to increment the counter"); + println!(); + App::new() - .add_plugins(DefaultPlugins) - .init_resource::() + .add_plugins(DefaultPlugins.set(bevy::render::RenderPlugin { + wgpu_settings: bevy::render::settings::WgpuSettings { + backends: Some(bevy::render::settings::Backends::PRIMARY), + ..Default::default() + }, + })) .add_system( increment_input_counter + // The common conditions module has a few useful run conditions + // for checking resources and states, these are included in prelude .run_if(resource_exists::()) - .run_if(is_input), + // This is our custom run condition, both this and the + // above condition must be true for the system to run + .run_if(has_user_input), ) .add_system( print_input_counter - .run_if(resource_exists::()) - .run_if(|c: Res| c.is_changed() && !c.is_added()), + // This is also a custom run condition but this time in the form of a closure, + // this is useful for small, simple run conditions you don't need to reuse. + // All the normal rules still apply, all parameters must be read only except for local parameters + // In this case we will only run if the input counter resource exists and has changed but not just been added + .run_if(|res: Option>| { + if let Some(counter) = res { + counter.is_changed() && !counter.is_added() + } else { + false + } + }), + ) + .add_system( + add_input_counter + // This is a custom generator function that returns a run + // condition, must like the common conditions module. + // It will only return true once 2 seconds has passed + .run_if(time_passed(2.0)), ) .run(); } @@ -22,21 +51,48 @@ fn main() { #[derive(Resource, Default)] struct InputCounter(usize); -// Return true if the user has clicked, tapped or pressed the space bar -fn is_input( +/// Return true if any of the defined inputs were just pressed +/// This is a custom run condition, it can take any normal system parameters as long as +/// they are read only except for local parameters which can be mutable +/// It returns a bool which determines if the system should run +fn has_user_input( keyboard_input: Res>, mouse_button_input: Res>, touch_input: Res, ) -> bool { keyboard_input.just_pressed(KeyCode::Space) + || keyboard_input.just_pressed(KeyCode::Return) || mouse_button_input.just_pressed(MouseButton::Left) + || mouse_button_input.just_pressed(MouseButton::Right) || touch_input.any_just_pressed() } +/// This is a generator fuction that returns a closure which meets all the run condition rules +/// This is useful becuase you can reuse the same run condition but with different variables +/// This is how the common conditions module works +fn time_passed(t: f32) -> impl FnMut(Local, Res