Path: blob/main/crates/bevy_ecs/src/schedule/condition.rs
6849 views
use alloc::{boxed::Box, format};1use bevy_utils::prelude::DebugName;2use core::ops::Not;34use crate::system::{5Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, RunSystemError,6System, SystemIn, SystemInput,7};89/// A type-erased run condition stored in a [`Box`].10pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;1112/// A system that determines if one or more scheduled systems should run.13///14/// Implemented for functions and closures that convert into [`System<Out=bool>`](System)15/// with [read-only](crate::system::ReadOnlySystemParam) parameters.16///17/// # Marker type parameter18///19/// `SystemCondition` trait has `Marker` type parameter, which has no special meaning,20/// but exists to work around the limitation of Rust's trait system.21///22/// Type parameter in return type can be set to `<()>` by calling [`IntoSystem::into_system`],23/// but usually have to be specified when passing a condition to a function.24///25/// ```26/// # use bevy_ecs::schedule::SystemCondition;27/// # use bevy_ecs::system::IntoSystem;28/// fn not_condition<Marker>(a: impl SystemCondition<Marker>) -> impl SystemCondition<()> {29/// IntoSystem::into_system(a.map(|x| !x))30/// }31/// ```32///33/// # Examples34/// A condition that returns true every other time it's called.35/// ```36/// # use bevy_ecs::prelude::*;37/// fn every_other_time() -> impl SystemCondition<()> {38/// IntoSystem::into_system(|mut flag: Local<bool>| {39/// *flag = !*flag;40/// *flag41/// })42/// }43///44/// # #[derive(Resource)] struct DidRun(bool);45/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }46/// # let mut schedule = Schedule::default();47/// schedule.add_systems(my_system.run_if(every_other_time()));48/// # let mut world = World::new();49/// # world.insert_resource(DidRun(false));50/// # schedule.run(&mut world);51/// # assert!(world.resource::<DidRun>().0);52/// # world.insert_resource(DidRun(false));53/// # schedule.run(&mut world);54/// # assert!(!world.resource::<DidRun>().0);55/// ```56///57/// A condition that takes a bool as an input and returns it unchanged.58///59/// ```60/// # use bevy_ecs::prelude::*;61/// fn identity() -> impl SystemCondition<(), In<bool>> {62/// IntoSystem::into_system(|In(x): In<bool>| x)63/// }64///65/// # fn always_true() -> bool { true }66/// # let mut app = Schedule::default();67/// # #[derive(Resource)] struct DidRun(bool);68/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }69/// app.add_systems(my_system.run_if(always_true.pipe(identity())));70/// # let mut world = World::new();71/// # world.insert_resource(DidRun(false));72/// # app.run(&mut world);73/// # assert!(world.resource::<DidRun>().0);74pub trait SystemCondition<Marker, In: SystemInput = ()>:75sealed::SystemCondition<Marker, In>76{77/// Returns a new run condition that only returns `true`78/// if both this one and the passed `and` return `true`.79///80/// The returned run condition is short-circuiting, meaning81/// `and` will only be invoked if `self` returns `true`.82///83/// # Examples84///85/// ```should_panic86/// use bevy_ecs::prelude::*;87///88/// #[derive(Resource, PartialEq)]89/// struct R(u32);90///91/// # let mut app = Schedule::default();92/// # let mut world = World::new();93/// # fn my_system() {}94/// app.add_systems(95/// // The `resource_equals` run condition will panic since we don't initialize `R`,96/// // just like if we used `Res<R>` in a system.97/// my_system.run_if(resource_equals(R(0))),98/// );99/// # app.run(&mut world);100/// ```101///102/// Use `.and()` to avoid checking the condition.103///104/// ```105/// # use bevy_ecs::prelude::*;106/// # #[derive(Resource, PartialEq)]107/// # struct R(u32);108/// # let mut app = Schedule::default();109/// # let mut world = World::new();110/// # fn my_system() {}111/// app.add_systems(112/// // `resource_equals` will only get run if the resource `R` exists.113/// my_system.run_if(resource_exists::<R>.and(resource_equals(R(0)))),114/// );115/// # app.run(&mut world);116/// ```117///118/// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].119///120/// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals121fn and<M, C: SystemCondition<M, In>>(self, and: C) -> And<Self::System, C::System> {122let a = IntoSystem::into_system(self);123let b = IntoSystem::into_system(and);124let name = format!("{} && {}", a.name(), b.name());125CombinatorSystem::new(a, b, DebugName::owned(name))126}127128/// Returns a new run condition that only returns `false`129/// if both this one and the passed `nand` return `true`.130///131/// The returned run condition is short-circuiting, meaning132/// `nand` will only be invoked if `self` returns `true`.133///134/// # Examples135///136/// ```compile_fail137/// use bevy::prelude::*;138///139/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]140/// pub enum PlayerState {141/// Alive,142/// Dead,143/// }144///145/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]146/// pub enum EnemyState {147/// Alive,148/// Dead,149/// }150///151/// # let mut app = Schedule::default();152/// # let mut world = World::new();153/// # fn game_over_credits() {}154/// app.add_systems(155/// // The game_over_credits system will only execute if either the `in_state(PlayerState::Alive)`156/// // run condition or `in_state(EnemyState::Alive)` run condition evaluates to `false`.157/// game_over_credits.run_if(158/// in_state(PlayerState::Alive).nand(in_state(EnemyState::Alive))159/// ),160/// );161/// # app.run(&mut world);162/// ```163///164/// Equivalent logic can be achieved by using `not` in concert with `and`:165///166/// ```compile_fail167/// app.add_systems(168/// game_over_credits.run_if(169/// not(in_state(PlayerState::Alive).and(in_state(EnemyState::Alive)))170/// ),171/// );172/// ```173fn nand<M, C: SystemCondition<M, In>>(self, nand: C) -> Nand<Self::System, C::System> {174let a = IntoSystem::into_system(self);175let b = IntoSystem::into_system(nand);176let name = format!("!({} && {})", a.name(), b.name());177CombinatorSystem::new(a, b, DebugName::owned(name))178}179180/// Returns a new run condition that only returns `true`181/// if both this one and the passed `nor` return `false`.182///183/// The returned run condition is short-circuiting, meaning184/// `nor` will only be invoked if `self` returns `false`.185///186/// # Examples187///188/// ```compile_fail189/// use bevy::prelude::*;190///191/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]192/// pub enum WeatherState {193/// Sunny,194/// Cloudy,195/// }196///197/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]198/// pub enum SoilState {199/// Fertilized,200/// NotFertilized,201/// }202///203/// # let mut app = Schedule::default();204/// # let mut world = World::new();205/// # fn slow_plant_growth() {}206/// app.add_systems(207/// // The slow_plant_growth system will only execute if both the `in_state(WeatherState::Sunny)`208/// // run condition and `in_state(SoilState::Fertilized)` run condition evaluate to `false`.209/// slow_plant_growth.run_if(210/// in_state(WeatherState::Sunny).nor(in_state(SoilState::Fertilized))211/// ),212/// );213/// # app.run(&mut world);214/// ```215///216/// Equivalent logic can be achieved by using `not` in concert with `or`:217///218/// ```compile_fail219/// app.add_systems(220/// slow_plant_growth.run_if(221/// not(in_state(WeatherState::Sunny).or(in_state(SoilState::Fertilized)))222/// ),223/// );224/// ```225fn nor<M, C: SystemCondition<M, In>>(self, nor: C) -> Nor<Self::System, C::System> {226let a = IntoSystem::into_system(self);227let b = IntoSystem::into_system(nor);228let name = format!("!({} || {})", a.name(), b.name());229CombinatorSystem::new(a, b, DebugName::owned(name))230}231232/// Returns a new run condition that returns `true`233/// if either this one or the passed `or` return `true`.234///235/// The returned run condition is short-circuiting, meaning236/// `or` will only be invoked if `self` returns `false`.237///238/// # Examples239///240/// ```241/// use bevy_ecs::prelude::*;242///243/// #[derive(Resource, PartialEq)]244/// struct A(u32);245///246/// #[derive(Resource, PartialEq)]247/// struct B(u32);248///249/// # let mut app = Schedule::default();250/// # let mut world = World::new();251/// # #[derive(Resource)] struct C(bool);252/// # fn my_system(mut c: ResMut<C>) { c.0 = true; }253/// app.add_systems(254/// // Only run the system if either `A` or `B` exist.255/// my_system.run_if(resource_exists::<A>.or(resource_exists::<B>)),256/// );257/// #258/// # world.insert_resource(C(false));259/// # app.run(&mut world);260/// # assert!(!world.resource::<C>().0);261/// #262/// # world.insert_resource(A(0));263/// # app.run(&mut world);264/// # assert!(world.resource::<C>().0);265/// #266/// # world.remove_resource::<A>();267/// # world.insert_resource(B(0));268/// # world.insert_resource(C(false));269/// # app.run(&mut world);270/// # assert!(world.resource::<C>().0);271/// ```272fn or<M, C: SystemCondition<M, In>>(self, or: C) -> Or<Self::System, C::System> {273let a = IntoSystem::into_system(self);274let b = IntoSystem::into_system(or);275let name = format!("{} || {}", a.name(), b.name());276CombinatorSystem::new(a, b, DebugName::owned(name))277}278279/// Returns a new run condition that only returns `true`280/// if `self` and `xnor` **both** return `false` or **both** return `true`.281///282/// # Examples283///284/// ```compile_fail285/// use bevy::prelude::*;286///287/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]288/// pub enum CoffeeMachineState {289/// Heating,290/// Brewing,291/// Inactive,292/// }293///294/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]295/// pub enum TeaKettleState {296/// Heating,297/// Steeping,298/// Inactive,299/// }300///301/// # let mut app = Schedule::default();302/// # let mut world = World::new();303/// # fn take_drink_orders() {}304/// app.add_systems(305/// // The take_drink_orders system will only execute if the `in_state(CoffeeMachineState::Inactive)`306/// // run condition and `in_state(TeaKettleState::Inactive)` run conditions both evaluate to `false`,307/// // or both evaluate to `true`.308/// take_drink_orders.run_if(309/// in_state(CoffeeMachineState::Inactive).xnor(in_state(TeaKettleState::Inactive))310/// ),311/// );312/// # app.run(&mut world);313/// ```314///315/// Equivalent logic can be achieved by using `not` in concert with `xor`:316///317/// ```compile_fail318/// app.add_systems(319/// take_drink_orders.run_if(320/// not(in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive)))321/// ),322/// );323/// ```324fn xnor<M, C: SystemCondition<M, In>>(self, xnor: C) -> Xnor<Self::System, C::System> {325let a = IntoSystem::into_system(self);326let b = IntoSystem::into_system(xnor);327let name = format!("!({} ^ {})", a.name(), b.name());328CombinatorSystem::new(a, b, DebugName::owned(name))329}330331/// Returns a new run condition that only returns `true`332/// if either `self` or `xor` return `true`, but not both.333///334/// # Examples335///336/// ```compile_fail337/// use bevy::prelude::*;338///339/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]340/// pub enum CoffeeMachineState {341/// Heating,342/// Brewing,343/// Inactive,344/// }345///346/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]347/// pub enum TeaKettleState {348/// Heating,349/// Steeping,350/// Inactive,351/// }352///353/// # let mut app = Schedule::default();354/// # let mut world = World::new();355/// # fn prepare_beverage() {}356/// app.add_systems(357/// // The prepare_beverage system will only execute if either the `in_state(CoffeeMachineState::Inactive)`358/// // run condition or `in_state(TeaKettleState::Inactive)` run condition evaluates to `true`,359/// // but not both.360/// prepare_beverage.run_if(361/// in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive))362/// ),363/// );364/// # app.run(&mut world);365/// ```366fn xor<M, C: SystemCondition<M, In>>(self, xor: C) -> Xor<Self::System, C::System> {367let a = IntoSystem::into_system(self);368let b = IntoSystem::into_system(xor);369let name = format!("({} ^ {})", a.name(), b.name());370CombinatorSystem::new(a, b, DebugName::owned(name))371}372}373374impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F where375F: sealed::SystemCondition<Marker, In>376{377}378379mod sealed {380use crate::system::{IntoSystem, ReadOnlySystem, SystemInput};381382pub trait SystemCondition<Marker, In: SystemInput>:383IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>384{385// This associated type is necessary to let the compiler386// know that `Self::System` is `ReadOnlySystem`.387type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;388}389390impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F391where392F: IntoSystem<In, bool, Marker>,393F::System: ReadOnlySystem,394{395type ReadOnlySystem = F::System;396}397}398399/// A collection of [run conditions](SystemCondition) that may be useful in any bevy app.400pub mod common_conditions {401use super::{NotSystem, SystemCondition};402use crate::{403change_detection::DetectChanges,404lifecycle::RemovedComponents,405message::{Message, MessageReader},406prelude::{Component, Query, With},407query::QueryFilter,408resource::Resource,409system::{In, IntoSystem, Local, Res, System, SystemInput},410};411use alloc::format;412413/// A [`SystemCondition`]-satisfying system that returns `true`414/// on the first time the condition is run and false every time after.415///416/// # Example417///418/// ```419/// # use bevy_ecs::prelude::*;420/// # #[derive(Resource, Default)]421/// # struct Counter(u8);422/// # let mut app = Schedule::default();423/// # let mut world = World::new();424/// # world.init_resource::<Counter>();425/// app.add_systems(426/// // `run_once` will only return true the first time it's evaluated427/// my_system.run_if(run_once),428/// );429///430/// fn my_system(mut counter: ResMut<Counter>) {431/// counter.0 += 1;432/// }433///434/// // This is the first time the condition will be evaluated so `my_system` will run435/// app.run(&mut world);436/// assert_eq!(world.resource::<Counter>().0, 1);437///438/// // This is the seconds time the condition will be evaluated so `my_system` won't run439/// app.run(&mut world);440/// assert_eq!(world.resource::<Counter>().0, 1);441/// ```442pub fn run_once(mut has_run: Local<bool>) -> bool {443if !*has_run {444*has_run = true;445true446} else {447false448}449}450451/// A [`SystemCondition`]-satisfying system that returns `true`452/// if the resource exists.453///454/// # Example455///456/// ```457/// # use bevy_ecs::prelude::*;458/// # #[derive(Resource, Default)]459/// # struct Counter(u8);460/// # let mut app = Schedule::default();461/// # let mut world = World::new();462/// app.add_systems(463/// // `resource_exists` will only return true if the given resource exists in the world464/// my_system.run_if(resource_exists::<Counter>),465/// );466///467/// fn my_system(mut counter: ResMut<Counter>) {468/// counter.0 += 1;469/// }470///471/// // `Counter` hasn't been added so `my_system` won't run472/// app.run(&mut world);473/// world.init_resource::<Counter>();474///475/// // `Counter` has now been added so `my_system` can run476/// app.run(&mut world);477/// assert_eq!(world.resource::<Counter>().0, 1);478/// ```479pub fn resource_exists<T>(res: Option<Res<T>>) -> bool480where481T: Resource,482{483res.is_some()484}485486/// Generates a [`SystemCondition`]-satisfying closure that returns `true`487/// if the resource is equal to `value`.488///489/// # Panics490///491/// The condition will panic if the resource does not exist.492///493/// # Example494///495/// ```496/// # use bevy_ecs::prelude::*;497/// # #[derive(Resource, Default, PartialEq)]498/// # struct Counter(u8);499/// # let mut app = Schedule::default();500/// # let mut world = World::new();501/// # world.init_resource::<Counter>();502/// app.add_systems(503/// // `resource_equals` will only return true if the given resource equals the given value504/// my_system.run_if(resource_equals(Counter(0))),505/// );506///507/// fn my_system(mut counter: ResMut<Counter>) {508/// counter.0 += 1;509/// }510///511/// // `Counter` is `0` so `my_system` can run512/// app.run(&mut world);513/// assert_eq!(world.resource::<Counter>().0, 1);514///515/// // `Counter` is no longer `0` so `my_system` won't run516/// app.run(&mut world);517/// assert_eq!(world.resource::<Counter>().0, 1);518/// ```519pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool520where521T: Resource + PartialEq,522{523move |res: Res<T>| *res == value524}525526/// Generates a [`SystemCondition`]-satisfying closure that returns `true`527/// if the resource exists and is equal to `value`.528///529/// The condition will return `false` if the resource does not exist.530///531/// # Example532///533/// ```534/// # use bevy_ecs::prelude::*;535/// # #[derive(Resource, Default, PartialEq)]536/// # struct Counter(u8);537/// # let mut app = Schedule::default();538/// # let mut world = World::new();539/// app.add_systems(540/// // `resource_exists_and_equals` will only return true541/// // if the given resource exists and equals the given value542/// my_system.run_if(resource_exists_and_equals(Counter(0))),543/// );544///545/// fn my_system(mut counter: ResMut<Counter>) {546/// counter.0 += 1;547/// }548///549/// // `Counter` hasn't been added so `my_system` can't run550/// app.run(&mut world);551/// world.init_resource::<Counter>();552///553/// // `Counter` is `0` so `my_system` can run554/// app.run(&mut world);555/// assert_eq!(world.resource::<Counter>().0, 1);556///557/// // `Counter` is no longer `0` so `my_system` won't run558/// app.run(&mut world);559/// assert_eq!(world.resource::<Counter>().0, 1);560/// ```561pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool562where563T: Resource + PartialEq,564{565move |res: Option<Res<T>>| match res {566Some(res) => *res == value,567None => false,568}569}570571/// A [`SystemCondition`]-satisfying system that returns `true`572/// if the resource of the given type has been added since the condition was last checked.573///574/// # Example575///576/// ```577/// # use bevy_ecs::prelude::*;578/// # #[derive(Resource, Default)]579/// # struct Counter(u8);580/// # let mut app = Schedule::default();581/// # let mut world = World::new();582/// app.add_systems(583/// // `resource_added` will only return true if the584/// // given resource was just added585/// my_system.run_if(resource_added::<Counter>),586/// );587///588/// fn my_system(mut counter: ResMut<Counter>) {589/// counter.0 += 1;590/// }591///592/// world.init_resource::<Counter>();593///594/// // `Counter` was just added so `my_system` will run595/// app.run(&mut world);596/// assert_eq!(world.resource::<Counter>().0, 1);597///598/// // `Counter` was not just added so `my_system` will not run599/// app.run(&mut world);600/// assert_eq!(world.resource::<Counter>().0, 1);601/// ```602pub fn resource_added<T>(res: Option<Res<T>>) -> bool603where604T: Resource,605{606match res {607Some(res) => res.is_added(),608None => false,609}610}611612/// A [`SystemCondition`]-satisfying system that returns `true`613/// if the resource of the given type has been added or mutably dereferenced614/// since the condition was last checked.615///616/// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).617/// Bevy does not compare resources to their previous values.618///619/// # Panics620///621/// The condition will panic if the resource does not exist.622///623/// # Example624///625/// ```626/// # use bevy_ecs::prelude::*;627/// # #[derive(Resource, Default)]628/// # struct Counter(u8);629/// # let mut app = Schedule::default();630/// # let mut world = World::new();631/// # world.init_resource::<Counter>();632/// app.add_systems(633/// // `resource_changed` will only return true if the634/// // given resource was just changed (or added)635/// my_system.run_if(636/// resource_changed::<Counter>637/// // By default detecting changes will also trigger if the resource was638/// // just added, this won't work with my example so I will add a second639/// // condition to make sure the resource wasn't just added640/// .and(not(resource_added::<Counter>))641/// ),642/// );643///644/// fn my_system(mut counter: ResMut<Counter>) {645/// counter.0 += 1;646/// }647///648/// // `Counter` hasn't been changed so `my_system` won't run649/// app.run(&mut world);650/// assert_eq!(world.resource::<Counter>().0, 0);651///652/// world.resource_mut::<Counter>().0 = 50;653///654/// // `Counter` was just changed so `my_system` will run655/// app.run(&mut world);656/// assert_eq!(world.resource::<Counter>().0, 51);657/// ```658pub fn resource_changed<T>(res: Res<T>) -> bool659where660T: Resource,661{662res.is_changed()663}664665/// A [`SystemCondition`]-satisfying system that returns `true`666/// if the resource of the given type has been added or mutably dereferenced since the condition667/// was last checked.668///669/// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).670/// Bevy does not compare resources to their previous values.671///672/// The condition will return `false` if the resource does not exist.673///674/// # Example675///676/// ```677/// # use bevy_ecs::prelude::*;678/// # #[derive(Resource, Default)]679/// # struct Counter(u8);680/// # let mut app = Schedule::default();681/// # let mut world = World::new();682/// app.add_systems(683/// // `resource_exists_and_changed` will only return true if the684/// // given resource exists and was just changed (or added)685/// my_system.run_if(686/// resource_exists_and_changed::<Counter>687/// // By default detecting changes will also trigger if the resource was688/// // just added, this won't work with my example so I will add a second689/// // condition to make sure the resource wasn't just added690/// .and(not(resource_added::<Counter>))691/// ),692/// );693///694/// fn my_system(mut counter: ResMut<Counter>) {695/// counter.0 += 1;696/// }697///698/// // `Counter` doesn't exist so `my_system` won't run699/// app.run(&mut world);700/// world.init_resource::<Counter>();701///702/// // `Counter` hasn't been changed so `my_system` won't run703/// app.run(&mut world);704/// assert_eq!(world.resource::<Counter>().0, 0);705///706/// world.resource_mut::<Counter>().0 = 50;707///708/// // `Counter` was just changed so `my_system` will run709/// app.run(&mut world);710/// assert_eq!(world.resource::<Counter>().0, 51);711/// ```712pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool713where714T: Resource,715{716match res {717Some(res) => res.is_changed(),718None => false,719}720}721722/// A [`SystemCondition`]-satisfying system that returns `true`723/// if the resource of the given type has been added, removed or mutably dereferenced since the condition724/// was last checked.725///726/// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).727/// Bevy does not compare resources to their previous values.728///729/// The condition will return `false` if the resource does not exist.730///731/// # Example732///733/// ```734/// # use bevy_ecs::prelude::*;735/// # #[derive(Resource, Default)]736/// # struct Counter(u8);737/// # let mut app = Schedule::default();738/// # let mut world = World::new();739/// # world.init_resource::<Counter>();740/// app.add_systems(741/// // `resource_changed_or_removed` will only return true if the742/// // given resource was just changed or removed (or added)743/// my_system.run_if(744/// resource_changed_or_removed::<Counter>745/// // By default detecting changes will also trigger if the resource was746/// // just added, this won't work with my example so I will add a second747/// // condition to make sure the resource wasn't just added748/// .and(not(resource_added::<Counter>))749/// ),750/// );751///752/// #[derive(Resource, Default)]753/// struct MyResource;754///755/// // If `Counter` exists, increment it, otherwise insert `MyResource`756/// fn my_system(mut commands: Commands, mut counter: Option<ResMut<Counter>>) {757/// if let Some(mut counter) = counter {758/// counter.0 += 1;759/// } else {760/// commands.init_resource::<MyResource>();761/// }762/// }763///764/// // `Counter` hasn't been changed so `my_system` won't run765/// app.run(&mut world);766/// assert_eq!(world.resource::<Counter>().0, 0);767///768/// world.resource_mut::<Counter>().0 = 50;769///770/// // `Counter` was just changed so `my_system` will run771/// app.run(&mut world);772/// assert_eq!(world.resource::<Counter>().0, 51);773///774/// world.remove_resource::<Counter>();775///776/// // `Counter` was just removed so `my_system` will run777/// app.run(&mut world);778/// assert_eq!(world.contains_resource::<MyResource>(), true);779/// ```780pub fn resource_changed_or_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool781where782T: Resource,783{784if let Some(value) = res {785*existed = true;786value.is_changed()787} else if *existed {788*existed = false;789true790} else {791false792}793}794795/// A [`SystemCondition`]-satisfying system that returns `true`796/// if the resource of the given type has been removed since the condition was last checked.797///798/// # Example799///800/// ```801/// # use bevy_ecs::prelude::*;802/// # #[derive(Resource, Default)]803/// # struct Counter(u8);804/// # let mut app = Schedule::default();805/// # let mut world = World::new();806/// # world.init_resource::<Counter>();807/// app.add_systems(808/// // `resource_removed` will only return true if the809/// // given resource was just removed810/// my_system.run_if(resource_removed::<MyResource>),811/// );812///813/// #[derive(Resource, Default)]814/// struct MyResource;815///816/// fn my_system(mut counter: ResMut<Counter>) {817/// counter.0 += 1;818/// }819///820/// world.init_resource::<MyResource>();821///822/// // `MyResource` hasn't just been removed so `my_system` won't run823/// app.run(&mut world);824/// assert_eq!(world.resource::<Counter>().0, 0);825///826/// world.remove_resource::<MyResource>();827///828/// // `MyResource` was just removed so `my_system` will run829/// app.run(&mut world);830/// assert_eq!(world.resource::<Counter>().0, 1);831/// ```832pub fn resource_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool833where834T: Resource,835{836if res.is_some() {837*existed = true;838false839} else if *existed {840*existed = false;841true842} else {843false844}845}846847/// A [`SystemCondition`]-satisfying system that returns `true`848/// if there are any new events of the given type since it was last called.849///850/// # Example851///852/// ```853/// # use bevy_ecs::prelude::*;854/// # #[derive(Resource, Default)]855/// # struct Counter(u8);856/// # let mut app = Schedule::default();857/// # let mut world = World::new();858/// # world.init_resource::<Counter>();859/// # world.init_resource::<Messages<MyMessage>>();860/// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));861///862/// app.add_systems(863/// my_system.run_if(on_message::<MyMessage>),864/// );865///866/// #[derive(Message)]867/// struct MyMessage;868///869/// fn my_system(mut counter: ResMut<Counter>) {870/// counter.0 += 1;871/// }872///873/// // No new `MyMessage` events have been push so `my_system` won't run874/// app.run(&mut world);875/// assert_eq!(world.resource::<Counter>().0, 0);876///877/// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);878///879/// // A `MyMessage` event has been pushed so `my_system` will run880/// app.run(&mut world);881/// assert_eq!(world.resource::<Counter>().0, 1);882/// ```883#[deprecated(since = "0.17.0", note = "Use `on_message` instead.")]884pub fn on_event<T: Message>(reader: MessageReader<T>) -> bool {885on_message(reader)886}887888/// A [`SystemCondition`]-satisfying system that returns `true`889/// if there are any new messages of the given type since it was last called.890///891/// # Example892///893/// ```894/// # use bevy_ecs::prelude::*;895/// # #[derive(Resource, Default)]896/// # struct Counter(u8);897/// # let mut app = Schedule::default();898/// # let mut world = World::new();899/// # world.init_resource::<Counter>();900/// # world.init_resource::<Messages<MyMessage>>();901/// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));902///903/// app.add_systems(904/// my_system.run_if(on_message::<MyMessage>),905/// );906///907/// #[derive(Message)]908/// struct MyMessage;909///910/// fn my_system(mut counter: ResMut<Counter>) {911/// counter.0 += 1;912/// }913///914/// // No new `MyMessage` messages have been push so `my_system` won't run915/// app.run(&mut world);916/// assert_eq!(world.resource::<Counter>().0, 0);917///918/// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);919///920/// // A `MyMessage` message has been pushed so `my_system` will run921/// app.run(&mut world);922/// assert_eq!(world.resource::<Counter>().0, 1);923/// ```924pub fn on_message<M: Message>(mut reader: MessageReader<M>) -> bool {925// The messages need to be consumed, so that there are no false positives on subsequent926// calls of the run condition. Simply checking `is_empty` would not be enough.927// PERF: note that `count` is efficient (not actually looping/iterating),928// due to Bevy having a specialized implementation for messages.929reader.read().count() > 0930}931932/// A [`SystemCondition`]-satisfying system that returns `true`933/// if there are any entities with the given component type.934///935/// # Example936///937/// ```938/// # use bevy_ecs::prelude::*;939/// # #[derive(Resource, Default)]940/// # struct Counter(u8);941/// # let mut app = Schedule::default();942/// # let mut world = World::new();943/// # world.init_resource::<Counter>();944/// app.add_systems(945/// my_system.run_if(any_with_component::<MyComponent>),946/// );947///948/// #[derive(Component)]949/// struct MyComponent;950///951/// fn my_system(mut counter: ResMut<Counter>) {952/// counter.0 += 1;953/// }954///955/// // No entities exist yet with a `MyComponent` component so `my_system` won't run956/// app.run(&mut world);957/// assert_eq!(world.resource::<Counter>().0, 0);958///959/// world.spawn(MyComponent);960///961/// // An entities with `MyComponent` now exists so `my_system` will run962/// app.run(&mut world);963/// assert_eq!(world.resource::<Counter>().0, 1);964/// ```965pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {966!query.is_empty()967}968969/// A [`SystemCondition`]-satisfying system that returns `true`970/// if there are any entity with a component of the given type removed.971pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {972// `RemovedComponents` based on events and therefore events need to be consumed,973// so that there are no false positives on subsequent calls of the run condition.974// Simply checking `is_empty` would not be enough.975// PERF: note that `count` is efficient (not actually looping/iterating),976// due to Bevy having a specialized implementation for events.977removals.read().count() > 0978}979980/// A [`SystemCondition`]-satisfying system that returns `true`981/// if there are any entities that match the given [`QueryFilter`].982pub fn any_match_filter<F: QueryFilter>(query: Query<(), F>) -> bool {983!query.is_empty()984}985986/// Generates a [`SystemCondition`] that inverses the result of passed one.987///988/// # Example989///990/// ```991/// # use bevy_ecs::prelude::*;992/// # #[derive(Resource, Default)]993/// # struct Counter(u8);994/// # let mut app = Schedule::default();995/// # let mut world = World::new();996/// # world.init_resource::<Counter>();997/// app.add_systems(998/// // `not` will inverse any condition you pass in.999/// // Since the condition we choose always returns true1000/// // this system will never run1001/// my_system.run_if(not(always)),1002/// );1003///1004/// fn my_system(mut counter: ResMut<Counter>) {1005/// counter.0 += 1;1006/// }1007///1008/// fn always() -> bool {1009/// true1010/// }1011///1012/// app.run(&mut world);1013/// assert_eq!(world.resource::<Counter>().0, 0);1014/// ```1015pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>1016where1017TOut: core::ops::Not,1018T: IntoSystem<(), TOut, Marker>,1019{1020let condition = IntoSystem::into_system(condition);1021let name = format!("!{}", condition.name());1022NotSystem::new(super::NotMarker, condition, name.into())1023}10241025/// Generates a [`SystemCondition`] that returns true when the passed one changes.1026///1027/// The first time this is called, the passed condition is assumed to have been previously false.1028///1029/// # Example1030///1031/// ```1032/// # use bevy_ecs::prelude::*;1033/// # #[derive(Resource, Default)]1034/// # struct Counter(u8);1035/// # let mut app = Schedule::default();1036/// # let mut world = World::new();1037/// # world.init_resource::<Counter>();1038/// app.add_systems(1039/// my_system.run_if(condition_changed(resource_exists::<MyResource>)),1040/// );1041///1042/// #[derive(Resource)]1043/// struct MyResource;1044///1045/// fn my_system(mut counter: ResMut<Counter>) {1046/// counter.0 += 1;1047/// }1048///1049/// // `MyResource` is initially there, the inner condition is true, the system runs once1050/// world.insert_resource(MyResource);1051/// app.run(&mut world);1052/// assert_eq!(world.resource::<Counter>().0, 1);1053/// app.run(&mut world);1054/// assert_eq!(world.resource::<Counter>().0, 1);1055///1056/// // We remove `MyResource`, the inner condition is now false, the system runs one more time.1057/// world.remove_resource::<MyResource>();1058/// app.run(&mut world);1059/// assert_eq!(world.resource::<Counter>().0, 2);1060/// app.run(&mut world);1061/// assert_eq!(world.resource::<Counter>().0, 2);1062/// ```1063pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl SystemCondition<(), CIn>1064where1065CIn: SystemInput,1066C: SystemCondition<Marker, CIn>,1067{1068IntoSystem::into_system(condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {1069let changed = *prev != new;1070*prev = new;1071changed1072}))1073}10741075/// Generates a [`SystemCondition`] that returns true when the result of1076/// the passed one went from false to true since the last time this was called.1077///1078/// The first time this is called, the passed condition is assumed to have been previously false.1079///1080/// # Example1081///1082/// ```1083/// # use bevy_ecs::prelude::*;1084/// # #[derive(Resource, Default)]1085/// # struct Counter(u8);1086/// # let mut app = Schedule::default();1087/// # let mut world = World::new();1088/// # world.init_resource::<Counter>();1089/// app.add_systems(1090/// my_system.run_if(condition_changed_to(true, resource_exists::<MyResource>)),1091/// );1092///1093/// #[derive(Resource)]1094/// struct MyResource;1095///1096/// fn my_system(mut counter: ResMut<Counter>) {1097/// counter.0 += 1;1098/// }1099///1100/// // `MyResource` is initially there, the inner condition is true, the system runs once1101/// world.insert_resource(MyResource);1102/// app.run(&mut world);1103/// assert_eq!(world.resource::<Counter>().0, 1);1104/// app.run(&mut world);1105/// assert_eq!(world.resource::<Counter>().0, 1);1106///1107/// // We remove `MyResource`, the inner condition is now false, the system doesn't run.1108/// world.remove_resource::<MyResource>();1109/// app.run(&mut world);1110/// assert_eq!(world.resource::<Counter>().0, 1);1111///1112/// // We reinsert `MyResource` again, so the system will run one more time1113/// world.insert_resource(MyResource);1114/// app.run(&mut world);1115/// assert_eq!(world.resource::<Counter>().0, 2);1116/// app.run(&mut world);1117/// assert_eq!(world.resource::<Counter>().0, 2);1118/// ```1119pub fn condition_changed_to<Marker, CIn, C>(1120to: bool,1121condition: C,1122) -> impl SystemCondition<(), CIn>1123where1124CIn: SystemInput,1125C: SystemCondition<Marker, CIn>,1126{1127IntoSystem::into_system(condition.pipe(1128move |In(new): In<bool>, mut prev: Local<bool>| -> bool {1129let now_true = *prev != new && new == to;1130*prev = new;1131now_true1132},1133))1134}1135}11361137/// Invokes [`Not`] with the output of another system.1138///1139/// See [`common_conditions::not`] for examples.1140pub type NotSystem<S> = AdapterSystem<NotMarker, S>;11411142/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.1143#[doc(hidden)]1144#[derive(Clone, Copy)]1145pub struct NotMarker;11461147impl<S: System<Out: Not>> Adapt<S> for NotMarker {1148type In = S::In;1149type Out = <S::Out as Not>::Output;11501151fn adapt(1152&mut self,1153input: <Self::In as SystemInput>::Inner<'_>,1154run_system: impl FnOnce(SystemIn<'_, S>) -> Result<S::Out, RunSystemError>,1155) -> Result<Self::Out, RunSystemError> {1156run_system(input).map(Not::not)1157}1158}11591160/// Combines the outputs of two systems using the `&&` operator.1161pub type And<A, B> = CombinatorSystem<AndMarker, A, B>;11621163/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.1164pub type Nand<A, B> = CombinatorSystem<NandMarker, A, B>;11651166/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.1167pub type Nor<A, B> = CombinatorSystem<NorMarker, A, B>;11681169/// Combines the outputs of two systems using the `||` operator.1170pub type Or<A, B> = CombinatorSystem<OrMarker, A, B>;11711172/// Combines and inverts the outputs of two systems using the `^` and `!` operators.1173pub type Xnor<A, B> = CombinatorSystem<XnorMarker, A, B>;11741175/// Combines the outputs of two systems using the `^` operator.1176pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;11771178#[doc(hidden)]1179pub struct AndMarker;11801181impl<In, A, B> Combine<A, B> for AndMarker1182where1183for<'a> In: SystemInput<Inner<'a>: Copy>,1184A: System<In = In, Out = bool>,1185B: System<In = In, Out = bool>,1186{1187type In = In;1188type Out = bool;11891190fn combine<T>(1191input: <Self::In as SystemInput>::Inner<'_>,1192data: &mut T,1193a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,1194b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,1195) -> Result<Self::Out, RunSystemError> {1196Ok(a(input, data)? && b(input, data)?)1197}1198}11991200#[doc(hidden)]1201pub struct NandMarker;12021203impl<In, A, B> Combine<A, B> for NandMarker1204where1205for<'a> In: SystemInput<Inner<'a>: Copy>,1206A: System<In = In, Out = bool>,1207B: System<In = In, Out = bool>,1208{1209type In = In;1210type Out = bool;12111212fn combine<T>(1213input: <Self::In as SystemInput>::Inner<'_>,1214data: &mut T,1215a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,1216b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,1217) -> Result<Self::Out, RunSystemError> {1218Ok(!(a(input, data)? && b(input, data)?))1219}1220}12211222#[doc(hidden)]1223pub struct NorMarker;12241225impl<In, A, B> Combine<A, B> for NorMarker1226where1227for<'a> In: SystemInput<Inner<'a>: Copy>,1228A: System<In = In, Out = bool>,1229B: System<In = In, Out = bool>,1230{1231type In = In;1232type Out = bool;12331234fn combine<T>(1235input: <Self::In as SystemInput>::Inner<'_>,1236data: &mut T,1237a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,1238b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,1239) -> Result<Self::Out, RunSystemError> {1240Ok(!(a(input, data)? || b(input, data)?))1241}1242}12431244#[doc(hidden)]1245pub struct OrMarker;12461247impl<In, A, B> Combine<A, B> for OrMarker1248where1249for<'a> In: SystemInput<Inner<'a>: Copy>,1250A: System<In = In, Out = bool>,1251B: System<In = In, Out = bool>,1252{1253type In = In;1254type Out = bool;12551256fn combine<T>(1257input: <Self::In as SystemInput>::Inner<'_>,1258data: &mut T,1259a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,1260b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,1261) -> Result<Self::Out, RunSystemError> {1262Ok(a(input, data)? || b(input, data)?)1263}1264}12651266#[doc(hidden)]1267pub struct XnorMarker;12681269impl<In, A, B> Combine<A, B> for XnorMarker1270where1271for<'a> In: SystemInput<Inner<'a>: Copy>,1272A: System<In = In, Out = bool>,1273B: System<In = In, Out = bool>,1274{1275type In = In;1276type Out = bool;12771278fn combine<T>(1279input: <Self::In as SystemInput>::Inner<'_>,1280data: &mut T,1281a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,1282b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,1283) -> Result<Self::Out, RunSystemError> {1284Ok(!(a(input, data)? ^ b(input, data)?))1285}1286}12871288#[doc(hidden)]1289pub struct XorMarker;12901291impl<In, A, B> Combine<A, B> for XorMarker1292where1293for<'a> In: SystemInput<Inner<'a>: Copy>,1294A: System<In = In, Out = bool>,1295B: System<In = In, Out = bool>,1296{1297type In = In;1298type Out = bool;12991300fn combine<T>(1301input: <Self::In as SystemInput>::Inner<'_>,1302data: &mut T,1303a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,1304b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,1305) -> Result<Self::Out, RunSystemError> {1306Ok(a(input, data)? ^ b(input, data)?)1307}1308}13091310#[cfg(test)]1311mod tests {1312use super::{common_conditions::*, SystemCondition};1313use crate::error::{BevyError, DefaultErrorHandler, ErrorContext};1314use crate::{1315change_detection::ResMut,1316component::Component,1317message::Message,1318query::With,1319schedule::{IntoScheduleConfigs, Schedule},1320system::{IntoSystem, Local, Res, System},1321world::World,1322};1323use bevy_ecs_macros::{Resource, SystemSet};13241325#[derive(Resource, Default)]1326struct Counter(usize);13271328fn increment_counter(mut counter: ResMut<Counter>) {1329counter.0 += 1;1330}13311332fn double_counter(mut counter: ResMut<Counter>) {1333counter.0 *= 2;1334}13351336fn every_other_time(mut has_ran: Local<bool>) -> bool {1337*has_ran = !*has_ran;1338*has_ran1339}13401341#[test]1342fn run_condition() {1343let mut world = World::new();1344world.init_resource::<Counter>();1345let mut schedule = Schedule::default();13461347// Run every other cycle1348schedule.add_systems(increment_counter.run_if(every_other_time));13491350schedule.run(&mut world);1351schedule.run(&mut world);1352assert_eq!(world.resource::<Counter>().0, 1);1353schedule.run(&mut world);1354schedule.run(&mut world);1355assert_eq!(world.resource::<Counter>().0, 2);13561357// Run every other cycle opposite to the last one1358schedule.add_systems(increment_counter.run_if(not(every_other_time)));13591360schedule.run(&mut world);1361schedule.run(&mut world);1362assert_eq!(world.resource::<Counter>().0, 4);1363schedule.run(&mut world);1364schedule.run(&mut world);1365assert_eq!(world.resource::<Counter>().0, 6);1366}13671368#[test]1369fn run_condition_combinators() {1370let mut world = World::new();1371world.init_resource::<Counter>();1372let mut schedule = Schedule::default();13731374schedule.add_systems(1375(1376increment_counter.run_if(every_other_time.and(|| true)), // Run every odd cycle.1377increment_counter.run_if(every_other_time.nand(|| false)), // Always run.1378double_counter.run_if(every_other_time.nor(|| false)), // Run every even cycle.1379increment_counter.run_if(every_other_time.or(|| true)), // Always run.1380increment_counter.run_if(every_other_time.xnor(|| true)), // Run every odd cycle.1381double_counter.run_if(every_other_time.xnor(|| false)), // Run every even cycle.1382increment_counter.run_if(every_other_time.xor(|| false)), // Run every odd cycle.1383double_counter.run_if(every_other_time.xor(|| true)), // Run every even cycle.1384)1385.chain(),1386);13871388schedule.run(&mut world);1389assert_eq!(world.resource::<Counter>().0, 5);1390schedule.run(&mut world);1391assert_eq!(world.resource::<Counter>().0, 52);1392}13931394#[test]1395fn multiple_run_conditions() {1396let mut world = World::new();1397world.init_resource::<Counter>();1398let mut schedule = Schedule::default();13991400// Run every other cycle1401schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));1402// Never run1403schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));14041405schedule.run(&mut world);1406assert_eq!(world.resource::<Counter>().0, 1);1407schedule.run(&mut world);1408assert_eq!(world.resource::<Counter>().0, 1);1409}14101411#[test]1412fn multiple_run_conditions_is_and_operation() {1413let mut world = World::new();1414world.init_resource::<Counter>();14151416let mut schedule = Schedule::default();14171418// This should never run, if multiple run conditions worked1419// like an OR condition then it would always run1420schedule.add_systems(1421increment_counter1422.run_if(every_other_time)1423.run_if(not(every_other_time)),1424);14251426schedule.run(&mut world);1427assert_eq!(world.resource::<Counter>().0, 0);1428schedule.run(&mut world);1429assert_eq!(world.resource::<Counter>().0, 0);1430}1431#[derive(Component)]1432struct TestComponent;14331434#[derive(Message)]1435struct TestMessage;14361437#[derive(Resource)]1438struct TestResource(());14391440fn test_system() {}14411442// Ensure distributive_run_if compiles with the common conditions.1443#[test]1444fn distributive_run_if_compiles() {1445Schedule::default().add_systems(1446(test_system, test_system)1447.distributive_run_if(run_once)1448.distributive_run_if(resource_exists::<TestResource>)1449.distributive_run_if(resource_added::<TestResource>)1450.distributive_run_if(resource_changed::<TestResource>)1451.distributive_run_if(resource_exists_and_changed::<TestResource>)1452.distributive_run_if(resource_changed_or_removed::<TestResource>)1453.distributive_run_if(resource_removed::<TestResource>)1454.distributive_run_if(on_message::<TestMessage>)1455.distributive_run_if(any_with_component::<TestComponent>)1456.distributive_run_if(any_match_filter::<With<TestComponent>>)1457.distributive_run_if(not(run_once)),1458);1459}14601461#[test]1462fn run_if_error_contains_system() {1463let mut world = World::new();1464world.insert_resource(DefaultErrorHandler(my_error_handler));14651466#[derive(Resource)]1467struct MyResource;14681469fn condition(_res: Res<MyResource>) -> bool {1470true1471}14721473fn my_error_handler(_: BevyError, ctx: ErrorContext) {1474let a = IntoSystem::into_system(system_a);1475let b = IntoSystem::into_system(system_b);1476assert!(1477matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))1478);1479}14801481fn system_a() {}1482fn system_b() {}14831484let mut schedule = Schedule::default();1485schedule.add_systems(system_a.run_if(condition));1486schedule.run(&mut world);14871488#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]1489struct Set;14901491let mut schedule = Schedule::default();1492schedule1493.add_systems((system_b,).in_set(Set))1494.configure_sets(Set.run_if(condition));1495schedule.run(&mut world);1496}1497}149814991500