|
| 1 | +use bevy::{app::AppExit, ecs::schedule::ShouldRun, prelude::*}; |
| 2 | + |
| 3 | +/// A [SystemLabel] can be applied as a label to systems and system sets, |
| 4 | +/// which can then be referred to from other systems. |
| 5 | +/// This is useful in case a user wants to e.g. run _before_ or _after_ |
| 6 | +/// some label. |
| 7 | +/// `Clone`, `Hash`, `Debug`, `PartialEq`, `Eq`, are all required to derive |
| 8 | +/// [SystemLabel]. |
| 9 | +#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] |
| 10 | +struct Physics; |
| 11 | + |
| 12 | +#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] |
| 13 | +struct PostPhysics; |
| 14 | + |
| 15 | +/// Resource used to stop our example. |
| 16 | +#[derive(Default)] |
| 17 | +struct Done(bool); |
| 18 | + |
| 19 | +/// This is used to show that within a [SystemSet], individual systems can also |
| 20 | +/// be labelled, allowing further fine tuning of run ordering. |
| 21 | +#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] |
| 22 | +pub enum PhysicsSystem { |
| 23 | + UpdateVelocity, |
| 24 | + Movement, |
| 25 | +} |
| 26 | + |
| 27 | +/// This example realizes the following scheme: |
| 28 | +/// |
| 29 | +/// ```none |
| 30 | +/// Physics (Criteria: App has run < 1.0 seconds) |
| 31 | +/// \--> update_velocity (via label PhysicsSystem::UpdateVelocity) |
| 32 | +/// \--> movement (via label PhysicsSystem::Movement) |
| 33 | +/// PostPhysics (Criteria: Resource `done` is false) |
| 34 | +/// \--> collision || sfx |
| 35 | +/// Exit (Criteria: Resource `done` is true) |
| 36 | +/// \--> exit |
| 37 | +/// ``` |
| 38 | +/// |
| 39 | +/// The `Physics` label represents a [SystemSet] containing two systems. |
| 40 | +/// This set's criteria is to stop after a second has elapsed. |
| 41 | +/// The two systems (update_velocity, movement) runs in a specified order. |
| 42 | +/// |
| 43 | +/// Another label `PostPhysics` uses run criteria to only run after `Physics` has finished. |
| 44 | +/// This set's criteria is to run only when _not done_, as specified via a resource. |
| 45 | +/// The two systems here (collision, sfx) are not specified to run in any order, and the actual |
| 46 | +/// ordering can then change between invocations. |
| 47 | +/// |
| 48 | +/// Lastly a system with run criterion _done_ is used to exit the app. |
| 49 | +/// ``` |
| 50 | +fn main() { |
| 51 | + App::build() |
| 52 | + .add_plugins(DefaultPlugins) |
| 53 | + .init_resource::<Done>() |
| 54 | + // Note that the system sets added in this example set their run criteria explicitly. |
| 55 | + // See the `ecs/state.rs` example for a pattern where run criteria are set implicitly for common |
| 56 | + // use cases- typically state transitions. |
| 57 | + // Also note that a system set has a single run criterion at most, which means using `.with_run_criteria(...)` |
| 58 | + // after `SystemSet::on_update(...)` would override the state transition criterion. |
| 59 | + .add_system_set( |
| 60 | + SystemSet::new() |
| 61 | + // This label is added to all systems in this set. |
| 62 | + // The label can then be referred to elsewhere (other sets). |
| 63 | + .label(Physics) |
| 64 | + // This criteria ensures this whole system set only runs when this system's |
| 65 | + // output says so (ShouldRun::Yes) |
| 66 | + .with_run_criteria(run_for_a_second.system()) |
| 67 | + .with_system( |
| 68 | + update_velocity |
| 69 | + .system() |
| 70 | + // Only applied to the `update_velocity` system |
| 71 | + .label(PhysicsSystem::UpdateVelocity), |
| 72 | + ) |
| 73 | + .with_system( |
| 74 | + movement |
| 75 | + .system() |
| 76 | + // Only applied to the `movement` system |
| 77 | + .label(PhysicsSystem::Movement) |
| 78 | + // Enforce order within this system by specifying this |
| 79 | + .after(PhysicsSystem::UpdateVelocity), |
| 80 | + ), |
| 81 | + ) |
| 82 | + .add_system_set( |
| 83 | + SystemSet::new() |
| 84 | + .label(PostPhysics) |
| 85 | + // This whole set runs after `Physics` (which in this case is a label for |
| 86 | + // another set). |
| 87 | + // There is also `.before(..)`. |
| 88 | + .after(Physics) |
| 89 | + // This shows that we can modify existing run criteria results. |
| 90 | + // Here we create a _not done_ criteria by piping the output of |
| 91 | + // the `is_done` system and inverting the output. |
| 92 | + // Notice a string literal also works as a label. |
| 93 | + .with_run_criteria(RunCriteria::pipe("is_done_label", inverse.system())) |
| 94 | + // `collision` and `sfx` are not ordered with respect to |
| 95 | + // each other, and may run in any order |
| 96 | + .with_system(collision.system()) |
| 97 | + .with_system(sfx.system()), |
| 98 | + ) |
| 99 | + .add_system( |
| 100 | + exit.system() |
| 101 | + .after(PostPhysics) |
| 102 | + // Label the run criteria such that the `PostPhysics` set can reference it |
| 103 | + .with_run_criteria(is_done.system().label("is_done_label")), |
| 104 | + ) |
| 105 | + .run(); |
| 106 | +} |
| 107 | + |
| 108 | +/// Example of a run criteria. |
| 109 | +/// Here we only want to run for a second, then stop. |
| 110 | +fn run_for_a_second(time: Res<Time>, mut done: ResMut<Done>) -> ShouldRun { |
| 111 | + let elapsed = time.seconds_since_startup(); |
| 112 | + if elapsed < 1.0 { |
| 113 | + info!( |
| 114 | + "We should run again. Elapsed/remaining: {:.2}s/{:.2}s", |
| 115 | + elapsed, |
| 116 | + 1.0 - elapsed |
| 117 | + ); |
| 118 | + ShouldRun::Yes |
| 119 | + } else { |
| 120 | + done.0 = true; |
| 121 | + ShouldRun::No |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +/// Another run criteria, simply using a resource. |
| 126 | +fn is_done(done: Res<Done>) -> ShouldRun { |
| 127 | + if done.0 { |
| 128 | + ShouldRun::Yes |
| 129 | + } else { |
| 130 | + ShouldRun::No |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +/// Used with [RunCritera::pipe], inverts the result of the |
| 135 | +/// passed system. |
| 136 | +fn inverse(input: In<ShouldRun>) -> ShouldRun { |
| 137 | + match input.0 { |
| 138 | + ShouldRun::No => ShouldRun::Yes, |
| 139 | + ShouldRun::Yes => ShouldRun::No, |
| 140 | + _ => unreachable!(), |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +fn update_velocity() { |
| 145 | + info!("Updating velocity"); |
| 146 | +} |
| 147 | + |
| 148 | +fn movement() { |
| 149 | + info!("Updating movement"); |
| 150 | +} |
| 151 | + |
| 152 | +fn collision() { |
| 153 | + info!("Physics done- checking collisions"); |
| 154 | +} |
| 155 | + |
| 156 | +fn sfx() { |
| 157 | + info!("Physics done- playing some sfx"); |
| 158 | +} |
| 159 | + |
| 160 | +fn exit(mut app_exit_events: EventWriter<AppExit>) { |
| 161 | + info!("Exiting..."); |
| 162 | + app_exit_events.send(AppExit); |
| 163 | +} |
0 commit comments