bevy_ecs/schedule/condition.rs
1use std::borrow::Cow;
2use std::ops::Not;
3
4use crate::system::{
5 Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System,
6};
7
8/// A type-erased run condition stored in a [`Box`].
9pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
10
11/// A system that determines if one or more scheduled systems should run.
12///
13/// Implemented for functions and closures that convert into [`System<Out=bool>`](System)
14/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
15///
16/// # Marker type parameter
17///
18/// `Condition` trait has `Marker` type parameter, which has no special meaning,
19/// but exists to work around the limitation of Rust's trait system.
20///
21/// Type parameter in return type can be set to `<()>` by calling [`IntoSystem::into_system`],
22/// but usually have to be specified when passing a condition to a function.
23///
24/// ```
25/// # use bevy_ecs::schedule::Condition;
26/// # use bevy_ecs::system::IntoSystem;
27/// fn not_condition<Marker>(a: impl Condition<Marker>) -> impl Condition<()> {
28/// IntoSystem::into_system(a.map(|x| !x))
29/// }
30/// ```
31///
32/// # Examples
33/// A condition that returns true every other time it's called.
34/// ```
35/// # use bevy_ecs::prelude::*;
36/// fn every_other_time() -> impl Condition<()> {
37/// IntoSystem::into_system(|mut flag: Local<bool>| {
38/// *flag = !*flag;
39/// *flag
40/// })
41/// }
42///
43/// # #[derive(Resource)] struct DidRun(bool);
44/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
45/// # let mut schedule = Schedule::default();
46/// schedule.add_systems(my_system.run_if(every_other_time()));
47/// # let mut world = World::new();
48/// # world.insert_resource(DidRun(false));
49/// # schedule.run(&mut world);
50/// # assert!(world.resource::<DidRun>().0);
51/// # world.insert_resource(DidRun(false));
52/// # schedule.run(&mut world);
53/// # assert!(!world.resource::<DidRun>().0);
54/// ```
55///
56/// A condition that takes a bool as an input and returns it unchanged.
57///
58/// ```
59/// # use bevy_ecs::prelude::*;
60/// fn identity() -> impl Condition<(), bool> {
61/// IntoSystem::into_system(|In(x)| x)
62/// }
63///
64/// # fn always_true() -> bool { true }
65/// # let mut app = Schedule::default();
66/// # #[derive(Resource)] struct DidRun(bool);
67/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
68/// app.add_systems(my_system.run_if(always_true.pipe(identity())));
69/// # let mut world = World::new();
70/// # world.insert_resource(DidRun(false));
71/// # app.run(&mut world);
72/// # assert!(world.resource::<DidRun>().0);
73pub trait Condition<Marker, In = ()>: sealed::Condition<Marker, In> {
74 /// Returns a new run condition that only returns `true`
75 /// if both this one and the passed `and_then` return `true`.
76 ///
77 /// The returned run condition is short-circuiting, meaning
78 /// `and_then` will only be invoked if `self` returns `true`.
79 ///
80 /// # Examples
81 ///
82 /// ```should_panic
83 /// use bevy_ecs::prelude::*;
84 ///
85 /// #[derive(Resource, PartialEq)]
86 /// struct R(u32);
87 ///
88 /// # let mut app = Schedule::default();
89 /// # let mut world = World::new();
90 /// # fn my_system() {}
91 /// app.add_systems(
92 /// // The `resource_equals` run condition will panic since we don't initialize `R`,
93 /// // just like if we used `Res<R>` in a system.
94 /// my_system.run_if(resource_equals(R(0))),
95 /// );
96 /// # app.run(&mut world);
97 /// ```
98 ///
99 /// Use `.and_then()` to avoid checking the condition.
100 ///
101 /// ```
102 /// # use bevy_ecs::prelude::*;
103 /// # #[derive(Resource, PartialEq)]
104 /// # struct R(u32);
105 /// # let mut app = Schedule::default();
106 /// # let mut world = World::new();
107 /// # fn my_system() {}
108 /// app.add_systems(
109 /// // `resource_equals` will only get run if the resource `R` exists.
110 /// my_system.run_if(resource_exists::<R>.and_then(resource_equals(R(0)))),
111 /// );
112 /// # app.run(&mut world);
113 /// ```
114 ///
115 /// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
116 ///
117 /// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
118 fn and_then<M, C: Condition<M, In>>(self, and_then: C) -> AndThen<Self::System, C::System> {
119 let a = IntoSystem::into_system(self);
120 let b = IntoSystem::into_system(and_then);
121 let name = format!("{} && {}", a.name(), b.name());
122 CombinatorSystem::new(a, b, Cow::Owned(name))
123 }
124
125 /// Returns a new run condition that returns `true`
126 /// if either this one or the passed `or_else` return `true`.
127 ///
128 /// The returned run condition is short-circuiting, meaning
129 /// `or_else` will only be invoked if `self` returns `false`.
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// use bevy_ecs::prelude::*;
135 ///
136 /// #[derive(Resource, PartialEq)]
137 /// struct A(u32);
138 ///
139 /// #[derive(Resource, PartialEq)]
140 /// struct B(u32);
141 ///
142 /// # let mut app = Schedule::default();
143 /// # let mut world = World::new();
144 /// # #[derive(Resource)] struct C(bool);
145 /// # fn my_system(mut c: ResMut<C>) { c.0 = true; }
146 /// app.add_systems(
147 /// // Only run the system if either `A` or `B` exist.
148 /// my_system.run_if(resource_exists::<A>.or_else(resource_exists::<B>)),
149 /// );
150 /// #
151 /// # world.insert_resource(C(false));
152 /// # app.run(&mut world);
153 /// # assert!(!world.resource::<C>().0);
154 /// #
155 /// # world.insert_resource(A(0));
156 /// # app.run(&mut world);
157 /// # assert!(world.resource::<C>().0);
158 /// #
159 /// # world.remove_resource::<A>();
160 /// # world.insert_resource(B(0));
161 /// # world.insert_resource(C(false));
162 /// # app.run(&mut world);
163 /// # assert!(world.resource::<C>().0);
164 /// ```
165 fn or_else<M, C: Condition<M, In>>(self, or_else: C) -> OrElse<Self::System, C::System> {
166 let a = IntoSystem::into_system(self);
167 let b = IntoSystem::into_system(or_else);
168 let name = format!("{} || {}", a.name(), b.name());
169 CombinatorSystem::new(a, b, Cow::Owned(name))
170 }
171}
172
173impl<Marker, In, F> Condition<Marker, In> for F where F: sealed::Condition<Marker, In> {}
174
175mod sealed {
176 use crate::system::{IntoSystem, ReadOnlySystem};
177
178 pub trait Condition<Marker, In>:
179 IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
180 {
181 // This associated type is necessary to let the compiler
182 // know that `Self::System` is `ReadOnlySystem`.
183 type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;
184 }
185
186 impl<Marker, In, F> Condition<Marker, In> for F
187 where
188 F: IntoSystem<In, bool, Marker>,
189 F::System: ReadOnlySystem,
190 {
191 type ReadOnlySystem = F::System;
192 }
193}
194
195/// A collection of [run conditions](Condition) that may be useful in any bevy app.
196pub mod common_conditions {
197 use super::NotSystem;
198 use crate::{
199 change_detection::DetectChanges,
200 event::{Event, EventReader},
201 prelude::{Component, Query, With},
202 removal_detection::RemovedComponents,
203 system::{IntoSystem, Res, Resource, System},
204 };
205
206 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
207 /// if the first time the condition is run and false every time after
208 ///
209 /// # Example
210 ///
211 /// ```
212 /// # use bevy_ecs::prelude::*;
213 /// # #[derive(Resource, Default)]
214 /// # struct Counter(u8);
215 /// # let mut app = Schedule::default();
216 /// # let mut world = World::new();
217 /// # world.init_resource::<Counter>();
218 /// app.add_systems(
219 /// // `run_once` will only return true the first time it's evaluated
220 /// my_system.run_if(run_once()),
221 /// );
222 ///
223 /// fn my_system(mut counter: ResMut<Counter>) {
224 /// counter.0 += 1;
225 /// }
226 ///
227 /// // This is the first time the condition will be evaluated so `my_system` will run
228 /// app.run(&mut world);
229 /// assert_eq!(world.resource::<Counter>().0, 1);
230 ///
231 /// // This is the seconds time the condition will be evaluated so `my_system` won't run
232 /// app.run(&mut world);
233 /// assert_eq!(world.resource::<Counter>().0, 1);
234 /// ```
235 pub fn run_once() -> impl FnMut() -> bool + Clone {
236 let mut has_run = false;
237 move || {
238 if !has_run {
239 has_run = true;
240 true
241 } else {
242 false
243 }
244 }
245 }
246
247 /// A [`Condition`](super::Condition)-satisfying system that returns `true`
248 /// if the resource exists.
249 ///
250 /// # Example
251 ///
252 /// ```
253 /// # use bevy_ecs::prelude::*;
254 /// # #[derive(Resource, Default)]
255 /// # struct Counter(u8);
256 /// # let mut app = Schedule::default();
257 /// # let mut world = World::new();
258 /// app.add_systems(
259 /// // `resource_exists` will only return true if the given resource exists in the world
260 /// my_system.run_if(resource_exists::<Counter>),
261 /// );
262 ///
263 /// fn my_system(mut counter: ResMut<Counter>) {
264 /// counter.0 += 1;
265 /// }
266 ///
267 /// // `Counter` hasn't been added so `my_system` won't run
268 /// app.run(&mut world);
269 /// world.init_resource::<Counter>();
270 ///
271 /// // `Counter` has now been added so `my_system` can run
272 /// app.run(&mut world);
273 /// assert_eq!(world.resource::<Counter>().0, 1);
274 /// ```
275 pub fn resource_exists<T>(res: Option<Res<T>>) -> bool
276 where
277 T: Resource,
278 {
279 res.is_some()
280 }
281
282 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
283 /// if the resource is equal to `value`.
284 ///
285 /// # Panics
286 ///
287 /// The condition will panic if the resource does not exist.
288 ///
289 /// # Example
290 ///
291 /// ```
292 /// # use bevy_ecs::prelude::*;
293 /// # #[derive(Resource, Default, PartialEq)]
294 /// # struct Counter(u8);
295 /// # let mut app = Schedule::default();
296 /// # let mut world = World::new();
297 /// # world.init_resource::<Counter>();
298 /// app.add_systems(
299 /// // `resource_equals` will only return true if the given resource equals the given value
300 /// my_system.run_if(resource_equals(Counter(0))),
301 /// );
302 ///
303 /// fn my_system(mut counter: ResMut<Counter>) {
304 /// counter.0 += 1;
305 /// }
306 ///
307 /// // `Counter` is `0` so `my_system` can run
308 /// app.run(&mut world);
309 /// assert_eq!(world.resource::<Counter>().0, 1);
310 ///
311 /// // `Counter` is no longer `0` so `my_system` won't run
312 /// app.run(&mut world);
313 /// assert_eq!(world.resource::<Counter>().0, 1);
314 /// ```
315 pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool
316 where
317 T: Resource + PartialEq,
318 {
319 move |res: Res<T>| *res == value
320 }
321
322 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
323 /// if the resource exists and is equal to `value`.
324 ///
325 /// The condition will return `false` if the resource does not exist.
326 ///
327 /// # Example
328 ///
329 /// ```
330 /// # use bevy_ecs::prelude::*;
331 /// # #[derive(Resource, Default, PartialEq)]
332 /// # struct Counter(u8);
333 /// # let mut app = Schedule::default();
334 /// # let mut world = World::new();
335 /// app.add_systems(
336 /// // `resource_exists_and_equals` will only return true
337 /// // if the given resource exists and equals the given value
338 /// my_system.run_if(resource_exists_and_equals(Counter(0))),
339 /// );
340 ///
341 /// fn my_system(mut counter: ResMut<Counter>) {
342 /// counter.0 += 1;
343 /// }
344 ///
345 /// // `Counter` hasn't been added so `my_system` can't run
346 /// app.run(&mut world);
347 /// world.init_resource::<Counter>();
348 ///
349 /// // `Counter` is `0` so `my_system` can run
350 /// app.run(&mut world);
351 /// assert_eq!(world.resource::<Counter>().0, 1);
352 ///
353 /// // `Counter` is no longer `0` so `my_system` won't run
354 /// app.run(&mut world);
355 /// assert_eq!(world.resource::<Counter>().0, 1);
356 /// ```
357 pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool
358 where
359 T: Resource + PartialEq,
360 {
361 move |res: Option<Res<T>>| match res {
362 Some(res) => *res == value,
363 None => false,
364 }
365 }
366
367 /// A [`Condition`](super::Condition)-satisfying system that returns `true`
368 /// if the resource of the given type has been added since the condition was last checked.
369 ///
370 /// # Example
371 ///
372 /// ```
373 /// # use bevy_ecs::prelude::*;
374 /// # #[derive(Resource, Default)]
375 /// # struct Counter(u8);
376 /// # let mut app = Schedule::default();
377 /// # let mut world = World::new();
378 /// app.add_systems(
379 /// // `resource_added` will only return true if the
380 /// // given resource was just added
381 /// my_system.run_if(resource_added::<Counter>),
382 /// );
383 ///
384 /// fn my_system(mut counter: ResMut<Counter>) {
385 /// counter.0 += 1;
386 /// }
387 ///
388 /// world.init_resource::<Counter>();
389 ///
390 /// // `Counter` was just added so `my_system` will run
391 /// app.run(&mut world);
392 /// assert_eq!(world.resource::<Counter>().0, 1);
393 ///
394 /// // `Counter` was not just added so `my_system` will not run
395 /// app.run(&mut world);
396 /// assert_eq!(world.resource::<Counter>().0, 1);
397 /// ```
398 pub fn resource_added<T>(res: Option<Res<T>>) -> bool
399 where
400 T: Resource,
401 {
402 match res {
403 Some(res) => res.is_added(),
404 None => false,
405 }
406 }
407
408 /// A [`Condition`](super::Condition)-satisfying system that returns `true`
409 /// if the resource of the given type has had its value changed since the condition
410 /// was last checked.
411 ///
412 /// The value is considered changed when it is added. The first time this condition
413 /// is checked after the resource was added, it will return `true`.
414 /// Change detection behaves like this everywhere in Bevy.
415 ///
416 /// # Panics
417 ///
418 /// The condition will panic if the resource does not exist.
419 ///
420 /// # Example
421 ///
422 /// ```
423 /// # use bevy_ecs::prelude::*;
424 /// # #[derive(Resource, Default)]
425 /// # struct Counter(u8);
426 /// # let mut app = Schedule::default();
427 /// # let mut world = World::new();
428 /// # world.init_resource::<Counter>();
429 /// app.add_systems(
430 /// // `resource_changed` will only return true if the
431 /// // given resource was just changed (or added)
432 /// my_system.run_if(
433 /// resource_changed::<Counter>
434 /// // By default detecting changes will also trigger if the resource was
435 /// // just added, this won't work with my example so I will add a second
436 /// // condition to make sure the resource wasn't just added
437 /// .and_then(not(resource_added::<Counter>))
438 /// ),
439 /// );
440 ///
441 /// fn my_system(mut counter: ResMut<Counter>) {
442 /// counter.0 += 1;
443 /// }
444 ///
445 /// // `Counter` hasn't been changed so `my_system` won't run
446 /// app.run(&mut world);
447 /// assert_eq!(world.resource::<Counter>().0, 0);
448 ///
449 /// world.resource_mut::<Counter>().0 = 50;
450 ///
451 /// // `Counter` was just changed so `my_system` will run
452 /// app.run(&mut world);
453 /// assert_eq!(world.resource::<Counter>().0, 51);
454 /// ```
455 pub fn resource_changed<T>(res: Res<T>) -> bool
456 where
457 T: Resource,
458 {
459 res.is_changed()
460 }
461
462 /// A [`Condition`](super::Condition)-satisfying system that returns `true`
463 /// if the resource of the given type has had its value changed since the condition
464 /// was last checked.
465 ///
466 /// The value is considered changed when it is added. The first time this condition
467 /// is checked after the resource was added, it will return `true`.
468 /// Change detection behaves like this everywhere in Bevy.
469 ///
470 /// This run condition does not detect when the resource is removed.
471 ///
472 /// The condition will return `false` if the resource does not exist.
473 ///
474 /// # Example
475 ///
476 /// ```
477 /// # use bevy_ecs::prelude::*;
478 /// # #[derive(Resource, Default)]
479 /// # struct Counter(u8);
480 /// # let mut app = Schedule::default();
481 /// # let mut world = World::new();
482 /// app.add_systems(
483 /// // `resource_exists_and_changed` will only return true if the
484 /// // given resource exists and was just changed (or added)
485 /// my_system.run_if(
486 /// resource_exists_and_changed::<Counter>
487 /// // By default detecting changes will also trigger if the resource was
488 /// // just added, this won't work with my example so I will add a second
489 /// // condition to make sure the resource wasn't just added
490 /// .and_then(not(resource_added::<Counter>))
491 /// ),
492 /// );
493 ///
494 /// fn my_system(mut counter: ResMut<Counter>) {
495 /// counter.0 += 1;
496 /// }
497 ///
498 /// // `Counter` doesn't exist so `my_system` won't run
499 /// app.run(&mut world);
500 /// world.init_resource::<Counter>();
501 ///
502 /// // `Counter` hasn't been changed so `my_system` won't run
503 /// app.run(&mut world);
504 /// assert_eq!(world.resource::<Counter>().0, 0);
505 ///
506 /// world.resource_mut::<Counter>().0 = 50;
507 ///
508 /// // `Counter` was just changed so `my_system` will run
509 /// app.run(&mut world);
510 /// assert_eq!(world.resource::<Counter>().0, 51);
511 /// ```
512 pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool
513 where
514 T: Resource,
515 {
516 match res {
517 Some(res) => res.is_changed(),
518 None => false,
519 }
520 }
521
522 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
523 /// if the resource of the given type has had its value changed since the condition
524 /// was last checked.
525 ///
526 /// The value is considered changed when it is added. The first time this condition
527 /// is checked after the resource was added, it will return `true`.
528 /// Change detection behaves like this everywhere in Bevy.
529 ///
530 /// This run condition also detects removal. It will return `true` if the resource
531 /// has been removed since the run condition was last checked.
532 ///
533 /// The condition will return `false` if the resource does not exist.
534 ///
535 /// # Example
536 ///
537 /// ```
538 /// # use bevy_ecs::prelude::*;
539 /// # #[derive(Resource, Default)]
540 /// # struct Counter(u8);
541 /// # let mut app = Schedule::default();
542 /// # let mut world = World::new();
543 /// # world.init_resource::<Counter>();
544 /// app.add_systems(
545 /// // `resource_changed_or_removed` will only return true if the
546 /// // given resource was just changed or removed (or added)
547 /// my_system.run_if(
548 /// resource_changed_or_removed::<Counter>()
549 /// // By default detecting changes will also trigger if the resource was
550 /// // just added, this won't work with my example so I will add a second
551 /// // condition to make sure the resource wasn't just added
552 /// .and_then(not(resource_added::<Counter>))
553 /// ),
554 /// );
555 ///
556 /// #[derive(Resource, Default)]
557 /// struct MyResource;
558 ///
559 /// // If `Counter` exists, increment it, otherwise insert `MyResource`
560 /// fn my_system(mut commands: Commands, mut counter: Option<ResMut<Counter>>) {
561 /// if let Some(mut counter) = counter {
562 /// counter.0 += 1;
563 /// } else {
564 /// commands.init_resource::<MyResource>();
565 /// }
566 /// }
567 ///
568 /// // `Counter` hasn't been changed so `my_system` won't run
569 /// app.run(&mut world);
570 /// assert_eq!(world.resource::<Counter>().0, 0);
571 ///
572 /// world.resource_mut::<Counter>().0 = 50;
573 ///
574 /// // `Counter` was just changed so `my_system` will run
575 /// app.run(&mut world);
576 /// assert_eq!(world.resource::<Counter>().0, 51);
577 ///
578 /// world.remove_resource::<Counter>();
579 ///
580 /// // `Counter` was just removed so `my_system` will run
581 /// app.run(&mut world);
582 /// assert_eq!(world.contains_resource::<MyResource>(), true);
583 /// ```
584 pub fn resource_changed_or_removed<T>() -> impl FnMut(Option<Res<T>>) -> bool + Clone
585 where
586 T: Resource,
587 {
588 let mut existed = false;
589 move |res: Option<Res<T>>| {
590 if let Some(value) = res {
591 existed = true;
592 value.is_changed()
593 } else if existed {
594 existed = false;
595 true
596 } else {
597 false
598 }
599 }
600 }
601
602 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
603 /// if the resource of the given type has been removed since the condition was last checked.
604 ///
605 /// # Example
606 ///
607 /// ```
608 /// # use bevy_ecs::prelude::*;
609 /// # #[derive(Resource, Default)]
610 /// # struct Counter(u8);
611 /// # let mut app = Schedule::default();
612 /// # let mut world = World::new();
613 /// # world.init_resource::<Counter>();
614 /// app.add_systems(
615 /// // `resource_removed` will only return true if the
616 /// // given resource was just removed
617 /// my_system.run_if(resource_removed::<MyResource>()),
618 /// );
619 ///
620 /// #[derive(Resource, Default)]
621 /// struct MyResource;
622 ///
623 /// fn my_system(mut counter: ResMut<Counter>) {
624 /// counter.0 += 1;
625 /// }
626 ///
627 /// world.init_resource::<MyResource>();
628 ///
629 /// // `MyResource` hasn't just been removed so `my_system` won't run
630 /// app.run(&mut world);
631 /// assert_eq!(world.resource::<Counter>().0, 0);
632 ///
633 /// world.remove_resource::<MyResource>();
634 ///
635 /// // `MyResource` was just removed so `my_system` will run
636 /// app.run(&mut world);
637 /// assert_eq!(world.resource::<Counter>().0, 1);
638 /// ```
639 pub fn resource_removed<T>() -> impl FnMut(Option<Res<T>>) -> bool + Clone
640 where
641 T: Resource,
642 {
643 let mut existed = false;
644 move |res: Option<Res<T>>| {
645 if res.is_some() {
646 existed = true;
647 false
648 } else if existed {
649 existed = false;
650 true
651 } else {
652 false
653 }
654 }
655 }
656
657 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
658 /// if there are any new events of the given type since it was last called.
659 ///
660 /// # Example
661 ///
662 /// ```
663 /// # use bevy_ecs::prelude::*;
664 /// # #[derive(Resource, Default)]
665 /// # struct Counter(u8);
666 /// # let mut app = Schedule::default();
667 /// # let mut world = World::new();
668 /// # world.init_resource::<Counter>();
669 /// # world.init_resource::<Events<MyEvent>>();
670 /// # app.add_systems(bevy_ecs::event::event_update_system.before(my_system));
671 ///
672 /// app.add_systems(
673 /// my_system.run_if(on_event::<MyEvent>()),
674 /// );
675 ///
676 /// #[derive(Event)]
677 /// struct MyEvent;
678 ///
679 /// fn my_system(mut counter: ResMut<Counter>) {
680 /// counter.0 += 1;
681 /// }
682 ///
683 /// // No new `MyEvent` events have been push so `my_system` won't run
684 /// app.run(&mut world);
685 /// assert_eq!(world.resource::<Counter>().0, 0);
686 ///
687 /// world.resource_mut::<Events<MyEvent>>().send(MyEvent);
688 ///
689 /// // A `MyEvent` event has been pushed so `my_system` will run
690 /// app.run(&mut world);
691 /// assert_eq!(world.resource::<Counter>().0, 1);
692 /// ```
693 pub fn on_event<T: Event>() -> impl FnMut(EventReader<T>) -> bool + Clone {
694 // The events need to be consumed, so that there are no false positives on subsequent
695 // calls of the run condition. Simply checking `is_empty` would not be enough.
696 // PERF: note that `count` is efficient (not actually looping/iterating),
697 // due to Bevy having a specialized implementation for events.
698 move |mut reader: EventReader<T>| reader.read().count() > 0
699 }
700
701 /// A [`Condition`](super::Condition)-satisfying system that returns `true`
702 /// if there are any entities with the given component type.
703 ///
704 /// # Example
705 ///
706 /// ```
707 /// # use bevy_ecs::prelude::*;
708 /// # #[derive(Resource, Default)]
709 /// # struct Counter(u8);
710 /// # let mut app = Schedule::default();
711 /// # let mut world = World::new();
712 /// # world.init_resource::<Counter>();
713 /// app.add_systems(
714 /// my_system.run_if(any_with_component::<MyComponent>),
715 /// );
716 ///
717 /// #[derive(Component)]
718 /// struct MyComponent;
719 ///
720 /// fn my_system(mut counter: ResMut<Counter>) {
721 /// counter.0 += 1;
722 /// }
723 ///
724 /// // No entities exist yet with a `MyComponent` component so `my_system` won't run
725 /// app.run(&mut world);
726 /// assert_eq!(world.resource::<Counter>().0, 0);
727 ///
728 /// world.spawn(MyComponent);
729 ///
730 /// // An entities with `MyComponent` now exists so `my_system` will run
731 /// app.run(&mut world);
732 /// assert_eq!(world.resource::<Counter>().0, 1);
733 /// ```
734 pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {
735 !query.is_empty()
736 }
737
738 /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
739 /// if there are any entity with a component of the given type removed.
740 pub fn any_component_removed<T: Component>() -> impl FnMut(RemovedComponents<T>) -> bool {
741 // `RemovedComponents` based on events and therefore events need to be consumed,
742 // so that there are no false positives on subsequent calls of the run condition.
743 // Simply checking `is_empty` would not be enough.
744 // PERF: note that `count` is efficient (not actually looping/iterating),
745 // due to Bevy having a specialized implementation for events.
746 move |mut removals: RemovedComponents<T>| removals.read().count() != 0
747 }
748
749 /// Generates a [`Condition`](super::Condition) that inverses the result of passed one.
750 ///
751 /// # Example
752 ///
753 /// ```
754 /// # use bevy_ecs::prelude::*;
755 /// # #[derive(Resource, Default)]
756 /// # struct Counter(u8);
757 /// # let mut app = Schedule::default();
758 /// # let mut world = World::new();
759 /// # world.init_resource::<Counter>();
760 /// app.add_systems(
761 /// // `not` will inverse any condition you pass in.
762 /// // Since the condition we choose always returns true
763 /// // this system will never run
764 /// my_system.run_if(not(always)),
765 /// );
766 ///
767 /// fn my_system(mut counter: ResMut<Counter>) {
768 /// counter.0 += 1;
769 /// }
770 ///
771 /// fn always() -> bool {
772 /// true
773 /// }
774 ///
775 /// app.run(&mut world);
776 /// assert_eq!(world.resource::<Counter>().0, 0);
777 /// ```
778 pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
779 where
780 TOut: std::ops::Not,
781 T: IntoSystem<(), TOut, Marker>,
782 {
783 let condition = IntoSystem::into_system(condition);
784 let name = format!("!{}", condition.name());
785 NotSystem::new(super::NotMarker, condition, name.into())
786 }
787}
788
789/// Invokes [`Not`] with the output of another system.
790///
791/// See [`common_conditions::not`] for examples.
792pub type NotSystem<T> = AdapterSystem<NotMarker, T>;
793
794/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.
795#[doc(hidden)]
796#[derive(Clone, Copy)]
797pub struct NotMarker;
798
799impl<T: System> Adapt<T> for NotMarker
800where
801 T::Out: Not,
802{
803 type In = T::In;
804 type Out = <T::Out as Not>::Output;
805
806 fn adapt(&mut self, input: Self::In, run_system: impl FnOnce(T::In) -> T::Out) -> Self::Out {
807 !run_system(input)
808 }
809}
810
811/// Combines the outputs of two systems using the `&&` operator.
812pub type AndThen<A, B> = CombinatorSystem<AndThenMarker, A, B>;
813
814/// Combines the outputs of two systems using the `||` operator.
815pub type OrElse<A, B> = CombinatorSystem<OrElseMarker, A, B>;
816
817#[doc(hidden)]
818pub struct AndThenMarker;
819
820impl<In, A, B> Combine<A, B> for AndThenMarker
821where
822 In: Copy,
823 A: System<In = In, Out = bool>,
824 B: System<In = In, Out = bool>,
825{
826 type In = In;
827 type Out = bool;
828
829 fn combine(
830 input: Self::In,
831 a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
832 b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
833 ) -> Self::Out {
834 a(input) && b(input)
835 }
836}
837
838#[doc(hidden)]
839pub struct OrElseMarker;
840
841impl<In, A, B> Combine<A, B> for OrElseMarker
842where
843 In: Copy,
844 A: System<In = In, Out = bool>,
845 B: System<In = In, Out = bool>,
846{
847 type In = In;
848 type Out = bool;
849
850 fn combine(
851 input: Self::In,
852 a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
853 b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
854 ) -> Self::Out {
855 a(input) || b(input)
856 }
857}
858
859#[cfg(test)]
860mod tests {
861 use super::{common_conditions::*, Condition};
862 use crate as bevy_ecs;
863 use crate::component::Component;
864 use crate::schedule::IntoSystemConfigs;
865 use crate::system::Local;
866 use crate::{change_detection::ResMut, schedule::Schedule, world::World};
867 use bevy_ecs_macros::Event;
868 use bevy_ecs_macros::Resource;
869
870 #[derive(Resource, Default)]
871 struct Counter(usize);
872
873 fn increment_counter(mut counter: ResMut<Counter>) {
874 counter.0 += 1;
875 }
876
877 fn every_other_time(mut has_ran: Local<bool>) -> bool {
878 *has_ran = !*has_ran;
879 *has_ran
880 }
881
882 #[test]
883 fn run_condition() {
884 let mut world = World::new();
885 world.init_resource::<Counter>();
886 let mut schedule = Schedule::default();
887
888 // Run every other cycle
889 schedule.add_systems(increment_counter.run_if(every_other_time));
890
891 schedule.run(&mut world);
892 schedule.run(&mut world);
893 assert_eq!(world.resource::<Counter>().0, 1);
894 schedule.run(&mut world);
895 schedule.run(&mut world);
896 assert_eq!(world.resource::<Counter>().0, 2);
897
898 // Run every other cycle opposite to the last one
899 schedule.add_systems(increment_counter.run_if(not(every_other_time)));
900
901 schedule.run(&mut world);
902 schedule.run(&mut world);
903 assert_eq!(world.resource::<Counter>().0, 4);
904 schedule.run(&mut world);
905 schedule.run(&mut world);
906 assert_eq!(world.resource::<Counter>().0, 6);
907 }
908
909 #[test]
910 fn run_condition_combinators() {
911 let mut world = World::new();
912 world.init_resource::<Counter>();
913 let mut schedule = Schedule::default();
914
915 // Always run
916 schedule.add_systems(increment_counter.run_if(every_other_time.or_else(|| true)));
917 // Run every other cycle
918 schedule.add_systems(increment_counter.run_if(every_other_time.and_then(|| true)));
919
920 schedule.run(&mut world);
921 assert_eq!(world.resource::<Counter>().0, 2);
922 schedule.run(&mut world);
923 assert_eq!(world.resource::<Counter>().0, 3);
924 }
925
926 #[test]
927 fn multiple_run_conditions() {
928 let mut world = World::new();
929 world.init_resource::<Counter>();
930 let mut schedule = Schedule::default();
931
932 // Run every other cycle
933 schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));
934 // Never run
935 schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));
936
937 schedule.run(&mut world);
938 assert_eq!(world.resource::<Counter>().0, 1);
939 schedule.run(&mut world);
940 assert_eq!(world.resource::<Counter>().0, 1);
941 }
942
943 #[test]
944 fn multiple_run_conditions_is_and_operation() {
945 let mut world = World::new();
946 world.init_resource::<Counter>();
947
948 let mut schedule = Schedule::default();
949
950 // This should never run, if multiple run conditions worked
951 // like an OR condition then it would always run
952 schedule.add_systems(
953 increment_counter
954 .run_if(every_other_time)
955 .run_if(not(every_other_time)),
956 );
957
958 schedule.run(&mut world);
959 assert_eq!(world.resource::<Counter>().0, 0);
960 schedule.run(&mut world);
961 assert_eq!(world.resource::<Counter>().0, 0);
962 }
963 #[derive(Component)]
964 struct TestComponent;
965
966 #[derive(Event)]
967 struct TestEvent;
968
969 #[derive(Resource)]
970 struct TestResource(());
971
972 fn test_system() {}
973
974 // Ensure distributive_run_if compiles with the common conditions.
975 #[test]
976 fn distributive_run_if_compiles() {
977 Schedule::default().add_systems(
978 (test_system, test_system)
979 .distributive_run_if(run_once())
980 .distributive_run_if(resource_exists::<TestResource>)
981 .distributive_run_if(resource_added::<TestResource>)
982 .distributive_run_if(resource_changed::<TestResource>)
983 .distributive_run_if(resource_exists_and_changed::<TestResource>)
984 .distributive_run_if(resource_changed_or_removed::<TestResource>())
985 .distributive_run_if(resource_removed::<TestResource>())
986 .distributive_run_if(on_event::<TestEvent>())
987 .distributive_run_if(any_with_component::<TestComponent>)
988 .distributive_run_if(not(run_once())),
989 );
990 }
991}