Skip to content

Commit 17d1fa4

Browse files
committed
Add more "common run conditions" (#7579)
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
1 parent c4f0de5 commit 17d1fa4

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

crates/bevy_ecs/src/schedule/condition.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ mod sealed {
133133
pub mod common_conditions {
134134
use super::Condition;
135135
use crate::{
136+
change_detection::DetectChanges,
137+
event::{Event, EventReader},
138+
prelude::{Component, Query, With},
136139
schedule::{State, States},
137140
system::{In, IntoPipeSystem, ReadOnlySystem, Res, Resource},
138141
};
@@ -187,6 +190,107 @@ pub mod common_conditions {
187190
}
188191
}
189192

193+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
194+
/// if the resource of the given type has been added since the condition was last checked.
195+
pub fn resource_added<T>() -> impl FnMut(Option<Res<T>>) -> bool
196+
where
197+
T: Resource,
198+
{
199+
move |res: Option<Res<T>>| match res {
200+
Some(res) => res.is_added(),
201+
None => false,
202+
}
203+
}
204+
205+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
206+
/// if the resource of the given type has had its value changed since the condition
207+
/// was last checked.
208+
///
209+
/// The value is considered changed when it is added. The first time this condition
210+
/// is checked after the resource was added, it will return `true`.
211+
/// Change detection behaves like this everywhere in Bevy.
212+
///
213+
/// # Panics
214+
///
215+
/// The condition will panic if the resource does not exist.
216+
pub fn resource_changed<T>() -> impl FnMut(Res<T>) -> bool
217+
where
218+
T: Resource,
219+
{
220+
move |res: Res<T>| res.is_changed()
221+
}
222+
223+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
224+
/// if the resource of the given type has had its value changed since the condition
225+
/// was last checked.
226+
///
227+
/// The value is considered changed when it is added. The first time this condition
228+
/// is checked after the resource was added, it will return `true`.
229+
/// Change detection behaves like this everywhere in Bevy.
230+
///
231+
/// This run condition does not detect when the resource is removed.
232+
///
233+
/// The condition will return `false` if the resource does not exist.
234+
pub fn resource_exists_and_changed<T>() -> impl FnMut(Option<Res<T>>) -> bool
235+
where
236+
T: Resource,
237+
{
238+
move |res: Option<Res<T>>| match res {
239+
Some(res) => res.is_changed(),
240+
None => false,
241+
}
242+
}
243+
244+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
245+
/// if the resource of the given type has had its value changed since the condition
246+
/// was last checked.
247+
///
248+
/// The value is considered changed when it is added. The first time this condition
249+
/// is checked after the resource was added, it will return `true`.
250+
/// Change detection behaves like this everywhere in Bevy.
251+
///
252+
/// This run condition also detects removal. It will return `true` if the resource
253+
/// has been removed since the run condition was last checked.
254+
///
255+
/// The condition will return `false` if the resource does not exist.
256+
pub fn resource_changed_or_removed<T>() -> impl FnMut(Option<Res<T>>) -> bool
257+
where
258+
T: Resource,
259+
{
260+
let mut existed = false;
261+
move |res: Option<Res<T>>| {
262+
if let Some(value) = res {
263+
existed = true;
264+
value.is_changed()
265+
} else if existed {
266+
existed = false;
267+
true
268+
} else {
269+
false
270+
}
271+
}
272+
}
273+
274+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
275+
/// if the resource of the given type has been removed since the condition was last checked.
276+
pub fn resource_removed<T>() -> impl FnMut(Option<Res<T>>) -> bool
277+
where
278+
T: Resource,
279+
{
280+
let mut existed = false;
281+
move |res: Option<Res<T>>| {
282+
if res.is_some() {
283+
existed = true;
284+
false
285+
} else if existed {
286+
existed = false;
287+
true
288+
} else {
289+
false
290+
}
291+
}
292+
}
293+
190294
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
191295
/// if the state machine exists.
192296
pub fn state_exists<S: States>() -> impl FnMut(Option<Res<State<S>>>) -> bool {
@@ -216,6 +320,35 @@ pub mod common_conditions {
216320
}
217321
}
218322

323+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
324+
/// if the state machine changed state.
325+
///
326+
/// To do things on transitions to/from specific states, use their respective OnEnter/OnExit
327+
/// schedules. Use this run condition if you want to detect any change, regardless of the value.
328+
///
329+
/// # Panics
330+
///
331+
/// The condition will panic if the resource does not exist.
332+
pub fn state_changed<S: States>() -> impl FnMut(Res<State<S>>) -> bool {
333+
move |current_state: Res<State<S>>| current_state.is_changed()
334+
}
335+
336+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
337+
/// if there are any new events of the given type since it was last called.
338+
pub fn on_event<T: Event>() -> impl FnMut(EventReader<T>) -> bool {
339+
// The events need to be consumed, so that there are no false positives on subsequent
340+
// calls of the run condition. Simply checking `is_empty` would not be enough.
341+
// PERF: note that `count` is efficient (not actually looping/iterating),
342+
// due to Bevy having a specialized implementation for events.
343+
move |mut reader: EventReader<T>| reader.iter().count() > 0
344+
}
345+
346+
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
347+
/// if there are any entities with the given component type.
348+
pub fn any_with_component<T: Component>() -> impl FnMut(Query<(), With<T>>) -> bool {
349+
move |query: Query<(), With<T>>| !query.is_empty()
350+
}
351+
219352
/// Generates a [`Condition`](super::Condition) that inverses the result of passed one.
220353
///
221354
/// # Examples

0 commit comments

Comments
 (0)