use std::borrow::Cow; use std::ops::Not; use crate::system::{ Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System, }; /// A type-erased run condition stored in a [`Box`]. pub type BoxedCondition = Box>; /// A system that determines if one or more scheduled systems should run. /// /// Implemented for functions and closures that convert into [`System`](System) /// with [read-only](crate::system::ReadOnlySystemParam) parameters. /// /// # Marker type parameter /// /// `Condition` trait has `Marker` type parameter, which has no special meaning, /// but exists to work around the limitation of Rust's trait system. /// /// Type parameter in return type can be set to `<()>` by calling [`IntoSystem::into_system`], /// but usually have to be specified when passing a condition to a function. /// /// ``` /// # use bevy_ecs::schedule::Condition; /// # use bevy_ecs::system::IntoSystem; /// fn not_condition(a: impl Condition) -> impl Condition<()> { /// IntoSystem::into_system(a.map(|x| !x)) /// } /// ``` /// /// # Examples /// A condition that returns true every other time it's called. /// ``` /// # use bevy_ecs::prelude::*; /// fn every_other_time() -> impl Condition<()> { /// IntoSystem::into_system(|mut flag: Local| { /// *flag = !*flag; /// *flag /// }) /// } /// /// # #[derive(Resource)] struct DidRun(bool); /// # fn my_system(mut did_run: ResMut) { did_run.0 = true; } /// # let mut schedule = Schedule::default(); /// schedule.add_systems(my_system.run_if(every_other_time())); /// # let mut world = World::new(); /// # world.insert_resource(DidRun(false)); /// # schedule.run(&mut world); /// # assert!(world.resource::().0); /// # world.insert_resource(DidRun(false)); /// # schedule.run(&mut world); /// # assert!(!world.resource::().0); /// ``` /// /// A condition that takes a bool as an input and returns it unchanged. /// /// ``` /// # use bevy_ecs::prelude::*; /// fn identity() -> impl Condition<(), bool> { /// IntoSystem::into_system(|In(x)| x) /// } /// /// # fn always_true() -> bool { true } /// # let mut app = Schedule::default(); /// # #[derive(Resource)] struct DidRun(bool); /// # fn my_system(mut did_run: ResMut) { did_run.0 = true; } /// app.add_systems(my_system.run_if(always_true.pipe(identity()))); /// # let mut world = World::new(); /// # world.insert_resource(DidRun(false)); /// # app.run(&mut world); /// # assert!(world.resource::().0); pub trait Condition: sealed::Condition { /// Returns a new run condition that only returns `true` /// if both this one and the passed `and_then` return `true`. /// /// The returned run condition is short-circuiting, meaning /// `and_then` will only be invoked if `self` returns `true`. /// /// # Examples /// /// ```should_panic /// use bevy_ecs::prelude::*; /// /// #[derive(Resource, PartialEq)] /// struct R(u32); /// /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # fn my_system() {} /// app.add_systems( /// // The `resource_equals` run condition will panic since we don't initialize `R`, /// // just like if we used `Res` in a system. /// my_system.run_if(resource_equals(R(0))), /// ); /// # app.run(&mut world); /// ``` /// /// Use `.and_then()` to avoid checking the condition. /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, PartialEq)] /// # struct R(u32); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # fn my_system() {} /// app.add_systems( /// // `resource_equals` will only get run if the resource `R` exists. /// my_system.run_if(resource_exists::.and_then(resource_equals(R(0)))), /// ); /// # app.run(&mut world); /// ``` /// /// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`]. /// /// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals fn and_then>(self, and_then: C) -> AndThen { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(and_then); let name = format!("{} && {}", a.name(), b.name()); CombinatorSystem::new(a, b, Cow::Owned(name)) } /// Returns a new run condition that returns `true` /// if either this one or the passed `or_else` return `true`. /// /// The returned run condition is short-circuiting, meaning /// `or_else` will only be invoked if `self` returns `false`. /// /// # Examples /// /// ``` /// use bevy_ecs::prelude::*; /// /// #[derive(Resource, PartialEq)] /// struct A(u32); /// /// #[derive(Resource, PartialEq)] /// struct B(u32); /// /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # #[derive(Resource)] struct C(bool); /// # fn my_system(mut c: ResMut) { c.0 = true; } /// app.add_systems( /// // Only run the system if either `A` or `B` exist. /// my_system.run_if(resource_exists::.or_else(resource_exists::)), /// ); /// # /// # world.insert_resource(C(false)); /// # app.run(&mut world); /// # assert!(!world.resource::().0); /// # /// # world.insert_resource(A(0)); /// # app.run(&mut world); /// # assert!(world.resource::().0); /// # /// # world.remove_resource::(); /// # world.insert_resource(B(0)); /// # world.insert_resource(C(false)); /// # app.run(&mut world); /// # assert!(world.resource::().0); /// ``` fn or_else>(self, or_else: C) -> OrElse { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(or_else); let name = format!("{} || {}", a.name(), b.name()); CombinatorSystem::new(a, b, Cow::Owned(name)) } } impl Condition for F where F: sealed::Condition {} mod sealed { use crate::system::{IntoSystem, ReadOnlySystem}; pub trait Condition: IntoSystem { // This associated type is necessary to let the compiler // know that `Self::System` is `ReadOnlySystem`. type ReadOnlySystem: ReadOnlySystem; } impl Condition for F where F: IntoSystem, F::System: ReadOnlySystem, { type ReadOnlySystem = F::System; } } /// A collection of [run conditions](Condition) that may be useful in any bevy app. pub mod common_conditions { use bevy_utils::warn_once; use super::NotSystem; use crate::{ change_detection::DetectChanges, event::{Event, EventReader}, prelude::{Component, Query, With}, removal_detection::RemovedComponents, schedule::{State, States}, system::{IntoSystem, Res, Resource, System}, }; /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the first time the condition is run and false every time after /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// // `run_once` will only return true the first time it's evaluated /// my_system.run_if(run_once()), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // This is the first time the condition will be evaluated so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// // This is the seconds time the condition will be evaluated so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn run_once() -> impl FnMut() -> bool + Clone { let mut has_run = false; move || { if !has_run { has_run = true; true } else { false } } } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if the resource exists. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// app.add_systems( /// // `resource_exists` will only return true if the given resource exists in the world /// my_system.run_if(resource_exists::), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `Counter` hasn't been added so `my_system` won't run /// app.run(&mut world); /// world.init_resource::(); /// /// // `Counter` has now been added so `my_system` can run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn resource_exists(res: Option>) -> bool where T: Resource, { res.is_some() } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the resource is equal to `value`. /// /// # Panics /// /// The condition will panic if the resource does not exist. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default, PartialEq)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// // `resource_equals` will only return true if the given resource equals the given value /// my_system.run_if(resource_equals(Counter(0))), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `Counter` is `0` so `my_system` can run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// // `Counter` is no longer `0` so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn resource_equals(value: T) -> impl FnMut(Res) -> bool where T: Resource + PartialEq, { move |res: Res| *res == value } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the resource exists and is equal to `value`. /// /// The condition will return `false` if the resource does not exist. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default, PartialEq)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// app.add_systems( /// // `resource_exists_and_equals` will only return true /// // if the given resource exists and equals the given value /// my_system.run_if(resource_exists_and_equals(Counter(0))), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `Counter` hasn't been added so `my_system` can't run /// app.run(&mut world); /// world.init_resource::(); /// /// // `Counter` is `0` so `my_system` can run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// // `Counter` is no longer `0` so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn resource_exists_and_equals(value: T) -> impl FnMut(Option>) -> bool where T: Resource + PartialEq, { move |res: Option>| match res { Some(res) => *res == value, None => false, } } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if the resource of the given type has been added since the condition was last checked. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// app.add_systems( /// // `resource_added` will only return true if the /// // given resource was just added /// my_system.run_if(resource_added::), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// world.init_resource::(); /// /// // `Counter` was just added so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// // `Counter` was not just added so `my_system` will not run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn resource_added(res: Option>) -> bool where T: Resource, { match res { Some(res) => res.is_added(), None => false, } } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if the resource of the given type has had its value changed since the condition /// was last checked. /// /// The value is considered changed when it is added. The first time this condition /// is checked after the resource was added, it will return `true`. /// Change detection behaves like this everywhere in Bevy. /// /// # Panics /// /// The condition will panic if the resource does not exist. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// // `resource_changed` will only return true if the /// // given resource was just changed (or added) /// my_system.run_if( /// resource_changed:: /// // By default detecting changes will also trigger if the resource was /// // just added, this won't work with my example so I will add a second /// // condition to make sure the resource wasn't just added /// .and_then(not(resource_added::)) /// ), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `Counter` hasn't been changed so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.resource_mut::().0 = 50; /// /// // `Counter` was just changed so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 51); /// ``` pub fn resource_changed(res: Res) -> bool where T: Resource, { res.is_changed() } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if the resource of the given type has had its value changed since the condition /// was last checked. /// /// The value is considered changed when it is added. The first time this condition /// is checked after the resource was added, it will return `true`. /// Change detection behaves like this everywhere in Bevy. /// /// This run condition does not detect when the resource is removed. /// /// The condition will return `false` if the resource does not exist. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// app.add_systems( /// // `resource_exists_and_changed` will only return true if the /// // given resource exists and was just changed (or added) /// my_system.run_if( /// resource_exists_and_changed:: /// // By default detecting changes will also trigger if the resource was /// // just added, this won't work with my example so I will add a second /// // condition to make sure the resource wasn't just added /// .and_then(not(resource_added::)) /// ), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `Counter` doesn't exist so `my_system` won't run /// app.run(&mut world); /// world.init_resource::(); /// /// // `Counter` hasn't been changed so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.resource_mut::().0 = 50; /// /// // `Counter` was just changed so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 51); /// ``` pub fn resource_exists_and_changed(res: Option>) -> bool where T: Resource, { match res { Some(res) => res.is_changed(), None => false, } } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the resource of the given type has had its value changed since the condition /// was last checked. /// /// The value is considered changed when it is added. The first time this condition /// is checked after the resource was added, it will return `true`. /// Change detection behaves like this everywhere in Bevy. /// /// This run condition also detects removal. It will return `true` if the resource /// has been removed since the run condition was last checked. /// /// The condition will return `false` if the resource does not exist. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// // `resource_changed_or_removed` will only return true if the /// // given resource was just changed or removed (or added) /// my_system.run_if( /// resource_changed_or_removed::() /// // By default detecting changes will also trigger if the resource was /// // just added, this won't work with my example so I will add a second /// // condition to make sure the resource wasn't just added /// .and_then(not(resource_added::)) /// ), /// ); /// /// #[derive(Resource, Default)] /// struct MyResource; /// /// // If `Counter` exists, increment it, otherwise insert `MyResource` /// fn my_system(mut commands: Commands, mut counter: Option>) { /// if let Some(mut counter) = counter { /// counter.0 += 1; /// } else { /// commands.init_resource::(); /// } /// } /// /// // `Counter` hasn't been changed so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.resource_mut::().0 = 50; /// /// // `Counter` was just changed so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 51); /// /// world.remove_resource::(); /// /// // `Counter` was just removed so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.contains_resource::(), true); /// ``` pub fn resource_changed_or_removed() -> impl FnMut(Option>) -> bool + Clone where T: Resource, { let mut existed = false; move |res: Option>| { if let Some(value) = res { existed = true; value.is_changed() } else if existed { existed = false; true } else { false } } } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the resource of the given type has been removed since the condition was last checked. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// // `resource_removed` will only return true if the /// // given resource was just removed /// my_system.run_if(resource_removed::()), /// ); /// /// #[derive(Resource, Default)] /// struct MyResource; /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// world.init_resource::(); /// /// // `MyResource` hasn't just been removed so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.remove_resource::(); /// /// // `MyResource` was just removed so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn resource_removed() -> impl FnMut(Option>) -> bool + Clone where T: Resource, { let mut existed = false; move |res: Option>| { if res.is_some() { existed = true; false } else if existed { existed = false; true } else { false } } } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if the state machine exists. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)] /// enum GameState { /// #[default] /// Playing, /// Paused, /// } /// /// app.add_systems( /// // `state_exists` will only return true if the /// // given state exists /// my_system.run_if(state_exists::), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `GameState` does not yet exist `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.init_resource::>(); /// /// // `GameState` now exists so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn state_exists(current_state: Option>>) -> bool { current_state.is_some() } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the state machine is currently in `state`. /// /// Will return `false` if the state does not exist or if not in `state`. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)] /// enum GameState { /// #[default] /// Playing, /// Paused, /// } /// /// world.init_resource::>(); /// /// app.add_systems(( /// // `in_state` will only return true if the /// // given state equals the given value /// play_system.run_if(in_state(GameState::Playing)), /// pause_system.run_if(in_state(GameState::Paused)), /// )); /// /// fn play_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// fn pause_system(mut counter: ResMut) { /// counter.0 -= 1; /// } /// /// // We default to `GameState::Playing` so `play_system` runs /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// *world.resource_mut::>() = State::new(GameState::Paused); /// /// // Now that we are in `GameState::Pause`, `pause_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// ``` pub fn in_state(state: S) -> impl FnMut(Option>>) -> bool + Clone { move |current_state: Option>>| match current_state { Some(current_state) => *current_state == state, None => { warn_once!("No state matching the type for {} exists - did you forget to `init_state` when initializing the app?", { let debug_state = format!("{state:?}"); let result = debug_state .split("::") .next() .unwrap_or("Unknown State Type"); result.to_string() }); false } } } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if the state machine changed state. /// /// To do things on transitions to/from specific states, use their respective OnEnter/OnExit /// schedules. Use this run condition if you want to detect any change, regardless of the value. /// /// Returns false if the state does not exist or the state has not changed. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)] /// enum GameState { /// #[default] /// Playing, /// Paused, /// } /// /// world.init_resource::>(); /// /// app.add_systems( /// // `state_changed` will only return true if the /// // given states value has just been updated or /// // the state has just been added /// my_system.run_if(state_changed::), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // `GameState` has just been added so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// // `GameState` has not been updated so `my_system` will not run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// /// *world.resource_mut::>() = State::new(GameState::Paused); /// /// // Now that `GameState` has been updated `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 2); /// ``` pub fn state_changed(current_state: Option>>) -> bool { let Some(current_state) = current_state else { return false; }; current_state.is_changed() } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if there are any new events of the given type since it was last called. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// # world.init_resource::>(); /// # app.add_systems(bevy_ecs::event::event_update_system.before(my_system)); /// /// app.add_systems( /// my_system.run_if(on_event::()), /// ); /// /// #[derive(Event)] /// struct MyEvent; /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // No new `MyEvent` events have been push so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.resource_mut::>().send(MyEvent); /// /// // A `MyEvent` event has been pushed so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn on_event() -> impl FnMut(EventReader) -> bool + Clone { // The events need to be consumed, so that there are no false positives on subsequent // calls of the run condition. Simply checking `is_empty` would not be enough. // PERF: note that `count` is efficient (not actually looping/iterating), // due to Bevy having a specialized implementation for events. move |mut reader: EventReader| reader.read().count() > 0 } /// A [`Condition`](super::Condition)-satisfying system that returns `true` /// if there are any entities with the given component type. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// my_system.run_if(any_with_component::), /// ); /// /// #[derive(Component)] /// struct MyComponent; /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// // No entities exist yet with a `MyComponent` component so `my_system` won't run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// /// world.spawn(MyComponent); /// /// // An entities with `MyComponent` now exists so `my_system` will run /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` pub fn any_with_component(query: Query<(), With>) -> bool { !query.is_empty() } /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if there are any entity with a component of the given type removed. pub fn any_component_removed() -> impl FnMut(RemovedComponents) -> bool { // `RemovedComponents` based on events and therefore events need to be consumed, // so that there are no false positives on subsequent calls of the run condition. // Simply checking `is_empty` would not be enough. // PERF: note that `count` is efficient (not actually looping/iterating), // due to Bevy having a specialized implementation for events. move |mut removals: RemovedComponents| removals.read().count() != 0 } /// Generates a [`Condition`](super::Condition) that inverses the result of passed one. /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Resource, Default)] /// # struct Counter(u8); /// # let mut app = Schedule::default(); /// # let mut world = World::new(); /// # world.init_resource::(); /// app.add_systems( /// // `not` will inverse any condition you pass in. /// // Since the condition we choose always returns true /// // this system will never run /// my_system.run_if(not(always)), /// ); /// /// fn my_system(mut counter: ResMut) { /// counter.0 += 1; /// } /// /// fn always() -> bool { /// true /// } /// /// app.run(&mut world); /// assert_eq!(world.resource::().0, 0); /// ``` pub fn not(condition: T) -> NotSystem where TOut: std::ops::Not, T: IntoSystem<(), TOut, Marker>, { let condition = IntoSystem::into_system(condition); let name = format!("!{}", condition.name()); NotSystem::new(super::NotMarker, condition, name.into()) } } /// Invokes [`Not`] with the output of another system. /// /// See [`common_conditions::not`] for examples. pub type NotSystem = AdapterSystem; /// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator. #[doc(hidden)] #[derive(Clone, Copy)] pub struct NotMarker; impl Adapt for NotMarker where T::Out: Not, { type In = T::In; type Out = ::Output; fn adapt(&mut self, input: Self::In, run_system: impl FnOnce(T::In) -> T::Out) -> Self::Out { !run_system(input) } } /// Combines the outputs of two systems using the `&&` operator. pub type AndThen = CombinatorSystem; /// Combines the outputs of two systems using the `||` operator. pub type OrElse = CombinatorSystem; #[doc(hidden)] pub struct AndThenMarker; impl Combine for AndThenMarker where In: Copy, A: System, B: System, { type In = In; type Out = bool; fn combine( input: Self::In, a: impl FnOnce(::In) -> ::Out, b: impl FnOnce(::In) -> ::Out, ) -> Self::Out { a(input) && b(input) } } #[doc(hidden)] pub struct OrElseMarker; impl Combine for OrElseMarker where In: Copy, A: System, B: System, { type In = In; type Out = bool; fn combine( input: Self::In, a: impl FnOnce(::In) -> ::Out, b: impl FnOnce(::In) -> ::Out, ) -> Self::Out { a(input) || b(input) } } #[cfg(test)] mod tests { use super::{common_conditions::*, Condition}; use crate as bevy_ecs; use crate::component::Component; use crate::schedule::IntoSystemConfigs; use crate::schedule::{State, States}; use crate::system::Local; use crate::{change_detection::ResMut, schedule::Schedule, world::World}; use bevy_ecs_macros::Event; use bevy_ecs_macros::Resource; #[derive(Resource, Default)] struct Counter(usize); fn increment_counter(mut counter: ResMut) { counter.0 += 1; } fn every_other_time(mut has_ran: Local) -> bool { *has_ran = !*has_ran; *has_ran } #[test] fn run_condition() { let mut world = World::new(); world.init_resource::(); let mut schedule = Schedule::default(); // Run every other cycle schedule.add_systems(increment_counter.run_if(every_other_time)); schedule.run(&mut world); schedule.run(&mut world); assert_eq!(world.resource::().0, 1); schedule.run(&mut world); schedule.run(&mut world); assert_eq!(world.resource::().0, 2); // Run every other cycle opposite to the last one schedule.add_systems(increment_counter.run_if(not(every_other_time))); schedule.run(&mut world); schedule.run(&mut world); assert_eq!(world.resource::().0, 4); schedule.run(&mut world); schedule.run(&mut world); assert_eq!(world.resource::().0, 6); } #[test] fn run_condition_combinators() { let mut world = World::new(); world.init_resource::(); let mut schedule = Schedule::default(); // Always run schedule.add_systems(increment_counter.run_if(every_other_time.or_else(|| true))); // Run every other cycle schedule.add_systems(increment_counter.run_if(every_other_time.and_then(|| true))); schedule.run(&mut world); assert_eq!(world.resource::().0, 2); schedule.run(&mut world); assert_eq!(world.resource::().0, 3); } #[test] fn multiple_run_conditions() { let mut world = World::new(); world.init_resource::(); let mut schedule = Schedule::default(); // Run every other cycle schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true)); // Never run schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false)); schedule.run(&mut world); assert_eq!(world.resource::().0, 1); schedule.run(&mut world); assert_eq!(world.resource::().0, 1); } #[test] fn multiple_run_conditions_is_and_operation() { let mut world = World::new(); world.init_resource::(); let mut schedule = Schedule::default(); // This should never run, if multiple run conditions worked // like an OR condition then it would always run schedule.add_systems( increment_counter .run_if(every_other_time) .run_if(not(every_other_time)), ); schedule.run(&mut world); assert_eq!(world.resource::().0, 0); schedule.run(&mut world); assert_eq!(world.resource::().0, 0); } #[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)] enum TestState { #[default] A, B, } #[derive(Component)] struct TestComponent; #[derive(Event)] struct TestEvent; fn test_system() {} // Ensure distributive_run_if compiles with the common conditions. #[test] fn distributive_run_if_compiles() { Schedule::default().add_systems( (test_system, test_system) .distributive_run_if(run_once()) .distributive_run_if(resource_exists::>) .distributive_run_if(resource_added::>) .distributive_run_if(resource_changed::>) .distributive_run_if(resource_exists_and_changed::>) .distributive_run_if(resource_changed_or_removed::>()) .distributive_run_if(resource_removed::>()) .distributive_run_if(state_exists::) .distributive_run_if(in_state(TestState::A).or_else(in_state(TestState::B))) .distributive_run_if(state_changed::) .distributive_run_if(on_event::()) .distributive_run_if(any_with_component::) .distributive_run_if(not(run_once())), ); } }