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.
pub mod common_conditions {
use super::NotSystem;
use super::{Condition, NotSystem};
use crate::{
change_detection::DetectChanges,
event::{Event, EventReader},
prelude::{Component, Query, With},
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.
///
/// # 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.
///
/// # Example
@ -572,7 +572,7 @@ pub mod common_conditions {
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`.
///
/// # Panics
@ -612,7 +612,7 @@ pub mod common_conditions {
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`.
///
/// 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.
///
/// # 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
/// was last checked.
///
@ -752,7 +752,7 @@ pub mod common_conditions {
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
/// 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
/// 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.
///
/// # 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.
///
/// # Example
@ -985,7 +985,7 @@ pub mod common_conditions {
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.
///
/// # Example
@ -1022,7 +1022,7 @@ pub mod common_conditions {
!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.
pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
// `RemovedComponents` based on events and therefore events need to be consumed,
@ -1033,7 +1033,7 @@ pub mod common_conditions {
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
///
@ -1071,6 +1071,109 @@ pub mod common_conditions {
let name = format!("!{}", condition.name());
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.