Add condition_changed and condition_became_true to common_conditions (#14917)

# Objective

- I needed to run a system whenever a specific condition became true
after being previously false.
- Other users might also need to run a system when a condition changes,
regardless of if it became true or false.

## Solution

- This adds two systems to common_conditions:
- `condition_changed` that changes whenever the inner condition changes
- `condition_became_true` that returns true whenever the inner condition
becomes true after previously being false

## Testing

- I added a doctest for each function

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
This commit is contained in:
Alix Bott 2024-08-26 20:32:44 +02:00 committed by GitHub
parent 23979b8160
commit 12f005a024
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -490,16 +490,16 @@ mod sealed {
/// A collection of [run conditions](Condition) that may be useful in any bevy app. /// A collection of [run conditions](Condition) that may be useful in any bevy app.
pub mod common_conditions { pub mod common_conditions {
use super::NotSystem; use super::{Condition, NotSystem};
use crate::{ use crate::{
change_detection::DetectChanges, change_detection::DetectChanges,
event::{Event, EventReader}, event::{Event, EventReader},
prelude::{Component, Query, With}, prelude::{Component, Query, With},
removal_detection::RemovedComponents, removal_detection::RemovedComponents,
system::{IntoSystem, Local, Res, Resource, System}, system::{In, IntoSystem, Local, Res, Resource, System},
}; };
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// on the first time the condition is run and false every time after. /// on the first time the condition is run and false every time after.
/// ///
/// # Example /// # Example
@ -537,7 +537,7 @@ pub mod common_conditions {
} }
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if the resource exists. /// if the resource exists.
/// ///
/// # Example /// # Example
@ -572,7 +572,7 @@ pub mod common_conditions {
res.is_some() res.is_some()
} }
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// Generates a [`Condition`]-satisfying closure that returns `true`
/// if the resource is equal to `value`. /// if the resource is equal to `value`.
/// ///
/// # Panics /// # Panics
@ -612,7 +612,7 @@ pub mod common_conditions {
move |res: Res<T>| *res == value move |res: Res<T>| *res == value
} }
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true` /// Generates a [`Condition`]-satisfying closure that returns `true`
/// if the resource exists and is equal to `value`. /// if the resource exists and is equal to `value`.
/// ///
/// The condition will return `false` if the resource does not exist. /// The condition will return `false` if the resource does not exist.
@ -657,7 +657,7 @@ pub mod common_conditions {
} }
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if the resource of the given type has been added since the condition was last checked. /// if the resource of the given type has been added since the condition was last checked.
/// ///
/// # Example /// # Example
@ -698,7 +698,7 @@ pub mod common_conditions {
} }
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if the resource of the given type has had its value changed since the condition /// if the resource of the given type has had its value changed since the condition
/// was last checked. /// was last checked.
/// ///
@ -752,7 +752,7 @@ pub mod common_conditions {
res.is_changed() res.is_changed()
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if the resource of the given type has had its value changed since the condition /// if the resource of the given type has had its value changed since the condition
/// was last checked. /// was last checked.
/// ///
@ -812,7 +812,7 @@ pub mod common_conditions {
} }
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if the resource of the given type has had its value changed since the condition /// if the resource of the given type has had its value changed since the condition
/// was last checked. /// was last checked.
/// ///
@ -889,7 +889,7 @@ pub mod common_conditions {
} }
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if the resource of the given type has been removed since the condition was last checked. /// if the resource of the given type has been removed since the condition was last checked.
/// ///
/// # Example /// # Example
@ -941,7 +941,7 @@ pub mod common_conditions {
} }
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if there are any new events of the given type since it was last called. /// if there are any new events of the given type since it was last called.
/// ///
/// # Example /// # Example
@ -985,7 +985,7 @@ pub mod common_conditions {
reader.read().count() > 0 reader.read().count() > 0
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if there are any entities with the given component type. /// if there are any entities with the given component type.
/// ///
/// # Example /// # Example
@ -1022,7 +1022,7 @@ pub mod common_conditions {
!query.is_empty() !query.is_empty()
} }
/// A [`Condition`](super::Condition)-satisfying system that returns `true` /// A [`Condition`]-satisfying system that returns `true`
/// if there are any entity with a component of the given type removed. /// if there are any entity with a component of the given type removed.
pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool { pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
// `RemovedComponents` based on events and therefore events need to be consumed, // `RemovedComponents` based on events and therefore events need to be consumed,
@ -1033,7 +1033,7 @@ pub mod common_conditions {
removals.read().count() > 0 removals.read().count() > 0
} }
/// Generates a [`Condition`](super::Condition) that inverses the result of passed one. /// Generates a [`Condition`] that inverses the result of passed one.
/// ///
/// # Example /// # Example
/// ///
@ -1071,6 +1071,109 @@ pub mod common_conditions {
let name = format!("!{}", condition.name()); let name = format!("!{}", condition.name());
NotSystem::new(super::NotMarker, condition, name.into()) NotSystem::new(super::NotMarker, condition, name.into())
} }
/// Generates a [`Condition`] that returns true when the passed one changes.
///
/// The first time this is called, the passed condition is assumed to have been previously false.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Resource, Default)]
/// # struct Counter(u8);
/// # let mut app = Schedule::default();
/// # let mut world = World::new();
/// # world.init_resource::<Counter>();
/// app.add_systems(
/// my_system.run_if(condition_changed(resource_exists::<MyResource>)),
/// );
///
/// #[derive(Resource)]
/// struct MyResource;
///
/// fn my_system(mut counter: ResMut<Counter>) {
/// counter.0 += 1;
/// }
///
/// // `MyResource` is initially there, the inner condition is true, the system runs once
/// world.insert_resource(MyResource);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 1);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 1);
///
/// // We remove `MyResource`, the inner condition is now false, the system runs one more time.
/// world.remove_resource::<MyResource>();
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 2);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 2);
/// ```
pub fn condition_changed<Marker, CIn, C: Condition<Marker, CIn>>(
condition: C,
) -> impl Condition<(), CIn> {
condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| -> bool {
let changed = *prev != new;
*prev = new;
changed
})
}
/// Generates a [`Condition`] that returns true when the result of
/// the passed one went from false to true since the last time this was called.
///
/// The first time this is called, the passed condition is assumed to have been previously false.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Resource, Default)]
/// # struct Counter(u8);
/// # let mut app = Schedule::default();
/// # let mut world = World::new();
/// # world.init_resource::<Counter>();
/// app.add_systems(
/// my_system.run_if(condition_changed_to(true, resource_exists::<MyResource>)),
/// );
///
/// #[derive(Resource)]
/// struct MyResource;
///
/// fn my_system(mut counter: ResMut<Counter>) {
/// counter.0 += 1;
/// }
///
/// // `MyResource` is initially there, the inner condition is true, the system runs once
/// world.insert_resource(MyResource);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 1);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 1);
///
/// // We remove `MyResource`, the inner condition is now false, the system doesn't run.
/// world.remove_resource::<MyResource>();
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 1);
///
/// // We reinsert `MyResource` again, so the system will run one more time
/// world.insert_resource(MyResource);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 2);
/// app.run(&mut world);
/// assert_eq!(world.resource::<Counter>().0, 2);
/// ```
pub fn condition_changed_to<Marker, CIn, C: Condition<Marker, CIn>>(
to: bool,
condition: C,
) -> impl Condition<(), CIn> {
condition.pipe(move |In(new): In<bool>, mut prev: Local<bool>| -> bool {
let now_true = *prev != new && new == to;
*prev = new;
now_true
})
}
} }
/// Invokes [`Not`] with the output of another system. /// Invokes [`Not`] with the output of another system.