From 17d1fa4a8b7dae7269b0dfcc53b23fdf90ff03f9 Mon Sep 17 00:00:00 2001 From: "Ida \"Iyes" Date: Mon, 20 Feb 2023 22:56:56 +0000 Subject: [PATCH] Add more "common run conditions" (#7579) Add some more useful common run conditions. Some of these existed in `iyes_loopless`. I know people used them, and it would be a regression for those users, when they try to migrate to new Bevy stageless, if they are missing. I also took the opportunity to add a few more new ones. --- ## Changelog ### Added - More "common run conditions": on_event, resource change detection, state_changed, any_with_component --- crates/bevy_ecs/src/schedule/condition.rs | 133 ++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index a3318f55144e3..1ecf637f6560c 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -133,6 +133,9 @@ mod sealed { pub mod common_conditions { use super::Condition; use crate::{ + change_detection::DetectChanges, + event::{Event, EventReader}, + prelude::{Component, Query, With}, schedule::{State, States}, system::{In, IntoPipeSystem, ReadOnlySystem, Res, Resource}, }; @@ -187,6 +190,107 @@ pub mod common_conditions { } } + /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` + /// if the resource of the given type has been added since the condition was last checked. + pub fn resource_added() -> impl FnMut(Option>) -> bool + where + T: Resource, + { + move |res: Option>| match res { + Some(res) => res.is_added(), + 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. + /// + /// # Panics + /// + /// The condition will panic if the resource does not exist. + pub fn resource_changed() -> impl FnMut(Res) -> bool + where + T: Resource, + { + move |res: Res| res.is_changed() + } + + /// 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 does not detect when the resource is removed. + /// + /// The condition will return `false` if the resource does not exist. + pub fn resource_exists_and_changed() -> impl FnMut(Option>) -> bool + where + T: Resource, + { + move |res: Option>| 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. + pub fn resource_changed_or_removed() -> impl FnMut(Option>) -> bool + 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. + pub fn resource_removed() -> impl FnMut(Option>) -> bool + 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 + } + } + } + /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// if the state machine exists. pub fn state_exists() -> impl FnMut(Option>>) -> bool { @@ -216,6 +320,35 @@ pub mod common_conditions { } } + /// Generates a [`Condition`](super::Condition)-satisfying closure 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. + /// + /// # Panics + /// + /// The condition will panic if the resource does not exist. + pub fn state_changed() -> impl FnMut(Res>) -> bool { + move |current_state: Res>| 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. + pub fn on_event() -> impl FnMut(EventReader) -> bool { + // 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.iter().count() > 0 + } + + /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` + /// if there are any entities with the given component type. + pub fn any_with_component() -> impl FnMut(Query<(), With>) -> bool { + move |query: Query<(), With>| !query.is_empty() + } + /// Generates a [`Condition`](super::Condition) that inverses the result of passed one. /// /// # Examples