From a4f1b5aefd99d34a26482a308e4733cbee46b6ca Mon Sep 17 00:00:00 2001 From: Ida Iyes Date: Thu, 9 Feb 2023 18:18:33 +0200 Subject: [PATCH 1/3] Add more "common run conditions": on_event, resource change detection, any_with_component --- crates/bevy_ecs/src/schedule/condition.rs | 72 +++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 763d88d8e0901..07c6d93ffabd5 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -26,6 +26,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}, }; @@ -80,6 +83,50 @@ 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. + pub fn resource_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 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 { @@ -109,6 +156,31 @@ 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 { + 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 From c3ed1c3d3c3ac70ed51b094013a83960196f23ec Mon Sep 17 00:00:00 2001 From: Ida Iyes Date: Tue, 21 Feb 2023 00:12:23 +0200 Subject: [PATCH 2/3] add more resource change detection run condition variants make the naming feel more consistent with the equality ones --- crates/bevy_ecs/src/schedule/condition.rs | 61 ++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 07c6d93ffabd5..3625e6b7b3506 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -96,8 +96,35 @@ pub mod common_conditions { } /// 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. - pub fn resource_changed() -> impl FnMut(Option>) -> bool + /// 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, { @@ -107,6 +134,36 @@ pub mod common_conditions { } } + /// 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 From 8bb7d6bda03847cd61b18bc23064d7f5c0077f85 Mon Sep 17 00:00:00 2001 From: Ida Iyes Date: Tue, 21 Feb 2023 00:12:58 +0200 Subject: [PATCH 3/3] add comment to `on_event` run condition to explain implementation decision --- crates/bevy_ecs/src/schedule/condition.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 3625e6b7b3506..8e3c722af743a 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -229,6 +229,10 @@ pub mod common_conditions { /// 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 }