Skip to content

Commit

Permalink
Add more "common run conditions" (#7579)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
inodentry committed Feb 20, 2023
1 parent c4f0de5 commit 17d1fa4
Showing 1 changed file with 133 additions and 0 deletions.
133 changes: 133 additions & 0 deletions crates/bevy_ecs/src/schedule/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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<T>() -> impl FnMut(Option<Res<T>>) -> bool
where
T: Resource,
{
move |res: Option<Res<T>>| 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<T>() -> impl FnMut(Res<T>) -> bool
where
T: Resource,
{
move |res: Res<T>| 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<T>() -> impl FnMut(Option<Res<T>>) -> bool
where
T: Resource,
{
move |res: Option<Res<T>>| 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<T>() -> impl FnMut(Option<Res<T>>) -> bool
where
T: Resource,
{
let mut existed = false;
move |res: Option<Res<T>>| {
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<T>() -> impl FnMut(Option<Res<T>>) -> bool
where
T: Resource,
{
let mut existed = false;
move |res: Option<Res<T>>| {
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<S: States>() -> impl FnMut(Option<Res<State<S>>>) -> bool {
Expand Down Expand Up @@ -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<S: States>() -> impl FnMut(Res<State<S>>) -> bool {
move |current_state: Res<State<S>>| 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<T: Event>() -> impl FnMut(EventReader<T>) -> 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<T>| 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<T: Component>() -> impl FnMut(Query<(), With<T>>) -> bool {
move |query: Query<(), With<T>>| !query.is_empty()
}

/// Generates a [`Condition`](super::Condition) that inverses the result of passed one.
///
/// # Examples
Expand Down

0 comments on commit 17d1fa4

Please sign in to comment.