Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add more "common run conditions" #7579

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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},
};
Expand Down Expand Up @@ -80,6 +83,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 @@ -109,6 +213,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
inodentry marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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