Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/schedule/condition.rs
6849 views
1
use alloc::{boxed::Box, format};
2
use bevy_utils::prelude::DebugName;
3
use core::ops::Not;
4
5
use crate::system::{
6
Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, RunSystemError,
7
System, SystemIn, SystemInput,
8
};
9
10
/// A type-erased run condition stored in a [`Box`].
11
pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
12
13
/// A system that determines if one or more scheduled systems should run.
14
///
15
/// Implemented for functions and closures that convert into [`System<Out=bool>`](System)
16
/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
17
///
18
/// # Marker type parameter
19
///
20
/// `SystemCondition` trait has `Marker` type parameter, which has no special meaning,
21
/// but exists to work around the limitation of Rust's trait system.
22
///
23
/// Type parameter in return type can be set to `<()>` by calling [`IntoSystem::into_system`],
24
/// but usually have to be specified when passing a condition to a function.
25
///
26
/// ```
27
/// # use bevy_ecs::schedule::SystemCondition;
28
/// # use bevy_ecs::system::IntoSystem;
29
/// fn not_condition<Marker>(a: impl SystemCondition<Marker>) -> impl SystemCondition<()> {
30
/// IntoSystem::into_system(a.map(|x| !x))
31
/// }
32
/// ```
33
///
34
/// # Examples
35
/// A condition that returns true every other time it's called.
36
/// ```
37
/// # use bevy_ecs::prelude::*;
38
/// fn every_other_time() -> impl SystemCondition<()> {
39
/// IntoSystem::into_system(|mut flag: Local<bool>| {
40
/// *flag = !*flag;
41
/// *flag
42
/// })
43
/// }
44
///
45
/// # #[derive(Resource)] struct DidRun(bool);
46
/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
47
/// # let mut schedule = Schedule::default();
48
/// schedule.add_systems(my_system.run_if(every_other_time()));
49
/// # let mut world = World::new();
50
/// # world.insert_resource(DidRun(false));
51
/// # schedule.run(&mut world);
52
/// # assert!(world.resource::<DidRun>().0);
53
/// # world.insert_resource(DidRun(false));
54
/// # schedule.run(&mut world);
55
/// # assert!(!world.resource::<DidRun>().0);
56
/// ```
57
///
58
/// A condition that takes a bool as an input and returns it unchanged.
59
///
60
/// ```
61
/// # use bevy_ecs::prelude::*;
62
/// fn identity() -> impl SystemCondition<(), In<bool>> {
63
/// IntoSystem::into_system(|In(x): In<bool>| x)
64
/// }
65
///
66
/// # fn always_true() -> bool { true }
67
/// # let mut app = Schedule::default();
68
/// # #[derive(Resource)] struct DidRun(bool);
69
/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
70
/// app.add_systems(my_system.run_if(always_true.pipe(identity())));
71
/// # let mut world = World::new();
72
/// # world.insert_resource(DidRun(false));
73
/// # app.run(&mut world);
74
/// # assert!(world.resource::<DidRun>().0);
75
pub trait SystemCondition<Marker, In: SystemInput = ()>:
76
sealed::SystemCondition<Marker, In>
77
{
78
/// Returns a new run condition that only returns `true`
79
/// if both this one and the passed `and` return `true`.
80
///
81
/// The returned run condition is short-circuiting, meaning
82
/// `and` will only be invoked if `self` returns `true`.
83
///
84
/// # Examples
85
///
86
/// ```should_panic
87
/// use bevy_ecs::prelude::*;
88
///
89
/// #[derive(Resource, PartialEq)]
90
/// struct R(u32);
91
///
92
/// # let mut app = Schedule::default();
93
/// # let mut world = World::new();
94
/// # fn my_system() {}
95
/// app.add_systems(
96
/// // The `resource_equals` run condition will panic since we don't initialize `R`,
97
/// // just like if we used `Res<R>` in a system.
98
/// my_system.run_if(resource_equals(R(0))),
99
/// );
100
/// # app.run(&mut world);
101
/// ```
102
///
103
/// Use `.and()` to avoid checking the condition.
104
///
105
/// ```
106
/// # use bevy_ecs::prelude::*;
107
/// # #[derive(Resource, PartialEq)]
108
/// # struct R(u32);
109
/// # let mut app = Schedule::default();
110
/// # let mut world = World::new();
111
/// # fn my_system() {}
112
/// app.add_systems(
113
/// // `resource_equals` will only get run if the resource `R` exists.
114
/// my_system.run_if(resource_exists::<R>.and(resource_equals(R(0)))),
115
/// );
116
/// # app.run(&mut world);
117
/// ```
118
///
119
/// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
120
///
121
/// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
122
fn and<M, C: SystemCondition<M, In>>(self, and: C) -> And<Self::System, C::System> {
123
let a = IntoSystem::into_system(self);
124
let b = IntoSystem::into_system(and);
125
let name = format!("{} && {}", a.name(), b.name());
126
CombinatorSystem::new(a, b, DebugName::owned(name))
127
}
128
129
/// Returns a new run condition that only returns `false`
130
/// if both this one and the passed `nand` return `true`.
131
///
132
/// The returned run condition is short-circuiting, meaning
133
/// `nand` will only be invoked if `self` returns `true`.
134
///
135
/// # Examples
136
///
137
/// ```compile_fail
138
/// use bevy::prelude::*;
139
///
140
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
141
/// pub enum PlayerState {
142
/// Alive,
143
/// Dead,
144
/// }
145
///
146
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
147
/// pub enum EnemyState {
148
/// Alive,
149
/// Dead,
150
/// }
151
///
152
/// # let mut app = Schedule::default();
153
/// # let mut world = World::new();
154
/// # fn game_over_credits() {}
155
/// app.add_systems(
156
/// // The game_over_credits system will only execute if either the `in_state(PlayerState::Alive)`
157
/// // run condition or `in_state(EnemyState::Alive)` run condition evaluates to `false`.
158
/// game_over_credits.run_if(
159
/// in_state(PlayerState::Alive).nand(in_state(EnemyState::Alive))
160
/// ),
161
/// );
162
/// # app.run(&mut world);
163
/// ```
164
///
165
/// Equivalent logic can be achieved by using `not` in concert with `and`:
166
///
167
/// ```compile_fail
168
/// app.add_systems(
169
/// game_over_credits.run_if(
170
/// not(in_state(PlayerState::Alive).and(in_state(EnemyState::Alive)))
171
/// ),
172
/// );
173
/// ```
174
fn nand<M, C: SystemCondition<M, In>>(self, nand: C) -> Nand<Self::System, C::System> {
175
let a = IntoSystem::into_system(self);
176
let b = IntoSystem::into_system(nand);
177
let name = format!("!({} && {})", a.name(), b.name());
178
CombinatorSystem::new(a, b, DebugName::owned(name))
179
}
180
181
/// Returns a new run condition that only returns `true`
182
/// if both this one and the passed `nor` return `false`.
183
///
184
/// The returned run condition is short-circuiting, meaning
185
/// `nor` will only be invoked if `self` returns `false`.
186
///
187
/// # Examples
188
///
189
/// ```compile_fail
190
/// use bevy::prelude::*;
191
///
192
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
193
/// pub enum WeatherState {
194
/// Sunny,
195
/// Cloudy,
196
/// }
197
///
198
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
199
/// pub enum SoilState {
200
/// Fertilized,
201
/// NotFertilized,
202
/// }
203
///
204
/// # let mut app = Schedule::default();
205
/// # let mut world = World::new();
206
/// # fn slow_plant_growth() {}
207
/// app.add_systems(
208
/// // The slow_plant_growth system will only execute if both the `in_state(WeatherState::Sunny)`
209
/// // run condition and `in_state(SoilState::Fertilized)` run condition evaluate to `false`.
210
/// slow_plant_growth.run_if(
211
/// in_state(WeatherState::Sunny).nor(in_state(SoilState::Fertilized))
212
/// ),
213
/// );
214
/// # app.run(&mut world);
215
/// ```
216
///
217
/// Equivalent logic can be achieved by using `not` in concert with `or`:
218
///
219
/// ```compile_fail
220
/// app.add_systems(
221
/// slow_plant_growth.run_if(
222
/// not(in_state(WeatherState::Sunny).or(in_state(SoilState::Fertilized)))
223
/// ),
224
/// );
225
/// ```
226
fn nor<M, C: SystemCondition<M, In>>(self, nor: C) -> Nor<Self::System, C::System> {
227
let a = IntoSystem::into_system(self);
228
let b = IntoSystem::into_system(nor);
229
let name = format!("!({} || {})", a.name(), b.name());
230
CombinatorSystem::new(a, b, DebugName::owned(name))
231
}
232
233
/// Returns a new run condition that returns `true`
234
/// if either this one or the passed `or` return `true`.
235
///
236
/// The returned run condition is short-circuiting, meaning
237
/// `or` will only be invoked if `self` returns `false`.
238
///
239
/// # Examples
240
///
241
/// ```
242
/// use bevy_ecs::prelude::*;
243
///
244
/// #[derive(Resource, PartialEq)]
245
/// struct A(u32);
246
///
247
/// #[derive(Resource, PartialEq)]
248
/// struct B(u32);
249
///
250
/// # let mut app = Schedule::default();
251
/// # let mut world = World::new();
252
/// # #[derive(Resource)] struct C(bool);
253
/// # fn my_system(mut c: ResMut<C>) { c.0 = true; }
254
/// app.add_systems(
255
/// // Only run the system if either `A` or `B` exist.
256
/// my_system.run_if(resource_exists::<A>.or(resource_exists::<B>)),
257
/// );
258
/// #
259
/// # world.insert_resource(C(false));
260
/// # app.run(&mut world);
261
/// # assert!(!world.resource::<C>().0);
262
/// #
263
/// # world.insert_resource(A(0));
264
/// # app.run(&mut world);
265
/// # assert!(world.resource::<C>().0);
266
/// #
267
/// # world.remove_resource::<A>();
268
/// # world.insert_resource(B(0));
269
/// # world.insert_resource(C(false));
270
/// # app.run(&mut world);
271
/// # assert!(world.resource::<C>().0);
272
/// ```
273
fn or<M, C: SystemCondition<M, In>>(self, or: C) -> Or<Self::System, C::System> {
274
let a = IntoSystem::into_system(self);
275
let b = IntoSystem::into_system(or);
276
let name = format!("{} || {}", a.name(), b.name());
277
CombinatorSystem::new(a, b, DebugName::owned(name))
278
}
279
280
/// Returns a new run condition that only returns `true`
281
/// if `self` and `xnor` **both** return `false` or **both** return `true`.
282
///
283
/// # Examples
284
///
285
/// ```compile_fail
286
/// use bevy::prelude::*;
287
///
288
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
289
/// pub enum CoffeeMachineState {
290
/// Heating,
291
/// Brewing,
292
/// Inactive,
293
/// }
294
///
295
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
296
/// pub enum TeaKettleState {
297
/// Heating,
298
/// Steeping,
299
/// Inactive,
300
/// }
301
///
302
/// # let mut app = Schedule::default();
303
/// # let mut world = World::new();
304
/// # fn take_drink_orders() {}
305
/// app.add_systems(
306
/// // The take_drink_orders system will only execute if the `in_state(CoffeeMachineState::Inactive)`
307
/// // run condition and `in_state(TeaKettleState::Inactive)` run conditions both evaluate to `false`,
308
/// // or both evaluate to `true`.
309
/// take_drink_orders.run_if(
310
/// in_state(CoffeeMachineState::Inactive).xnor(in_state(TeaKettleState::Inactive))
311
/// ),
312
/// );
313
/// # app.run(&mut world);
314
/// ```
315
///
316
/// Equivalent logic can be achieved by using `not` in concert with `xor`:
317
///
318
/// ```compile_fail
319
/// app.add_systems(
320
/// take_drink_orders.run_if(
321
/// not(in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive)))
322
/// ),
323
/// );
324
/// ```
325
fn xnor<M, C: SystemCondition<M, In>>(self, xnor: C) -> Xnor<Self::System, C::System> {
326
let a = IntoSystem::into_system(self);
327
let b = IntoSystem::into_system(xnor);
328
let name = format!("!({} ^ {})", a.name(), b.name());
329
CombinatorSystem::new(a, b, DebugName::owned(name))
330
}
331
332
/// Returns a new run condition that only returns `true`
333
/// if either `self` or `xor` return `true`, but not both.
334
///
335
/// # Examples
336
///
337
/// ```compile_fail
338
/// use bevy::prelude::*;
339
///
340
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
341
/// pub enum CoffeeMachineState {
342
/// Heating,
343
/// Brewing,
344
/// Inactive,
345
/// }
346
///
347
/// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
348
/// pub enum TeaKettleState {
349
/// Heating,
350
/// Steeping,
351
/// Inactive,
352
/// }
353
///
354
/// # let mut app = Schedule::default();
355
/// # let mut world = World::new();
356
/// # fn prepare_beverage() {}
357
/// app.add_systems(
358
/// // The prepare_beverage system will only execute if either the `in_state(CoffeeMachineState::Inactive)`
359
/// // run condition or `in_state(TeaKettleState::Inactive)` run condition evaluates to `true`,
360
/// // but not both.
361
/// prepare_beverage.run_if(
362
/// in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive))
363
/// ),
364
/// );
365
/// # app.run(&mut world);
366
/// ```
367
fn xor<M, C: SystemCondition<M, In>>(self, xor: C) -> Xor<Self::System, C::System> {
368
let a = IntoSystem::into_system(self);
369
let b = IntoSystem::into_system(xor);
370
let name = format!("({} ^ {})", a.name(), b.name());
371
CombinatorSystem::new(a, b, DebugName::owned(name))
372
}
373
}
374
375
impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F where
376
F: sealed::SystemCondition<Marker, In>
377
{
378
}
379
380
mod sealed {
381
use crate::system::{IntoSystem, ReadOnlySystem, SystemInput};
382
383
pub trait SystemCondition<Marker, In: SystemInput>:
384
IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
385
{
386
// This associated type is necessary to let the compiler
387
// know that `Self::System` is `ReadOnlySystem`.
388
type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;
389
}
390
391
impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F
392
where
393
F: IntoSystem<In, bool, Marker>,
394
F::System: ReadOnlySystem,
395
{
396
type ReadOnlySystem = F::System;
397
}
398
}
399
400
/// A collection of [run conditions](SystemCondition) that may be useful in any bevy app.
401
pub mod common_conditions {
402
use super::{NotSystem, SystemCondition};
403
use crate::{
404
change_detection::DetectChanges,
405
lifecycle::RemovedComponents,
406
message::{Message, MessageReader},
407
prelude::{Component, Query, With},
408
query::QueryFilter,
409
resource::Resource,
410
system::{In, IntoSystem, Local, Res, System, SystemInput},
411
};
412
use alloc::format;
413
414
/// A [`SystemCondition`]-satisfying system that returns `true`
415
/// on the first time the condition is run and false every time after.
416
///
417
/// # Example
418
///
419
/// ```
420
/// # use bevy_ecs::prelude::*;
421
/// # #[derive(Resource, Default)]
422
/// # struct Counter(u8);
423
/// # let mut app = Schedule::default();
424
/// # let mut world = World::new();
425
/// # world.init_resource::<Counter>();
426
/// app.add_systems(
427
/// // `run_once` will only return true the first time it's evaluated
428
/// my_system.run_if(run_once),
429
/// );
430
///
431
/// fn my_system(mut counter: ResMut<Counter>) {
432
/// counter.0 += 1;
433
/// }
434
///
435
/// // This is the first time the condition will be evaluated so `my_system` will run
436
/// app.run(&mut world);
437
/// assert_eq!(world.resource::<Counter>().0, 1);
438
///
439
/// // This is the seconds time the condition will be evaluated so `my_system` won't run
440
/// app.run(&mut world);
441
/// assert_eq!(world.resource::<Counter>().0, 1);
442
/// ```
443
pub fn run_once(mut has_run: Local<bool>) -> bool {
444
if !*has_run {
445
*has_run = true;
446
true
447
} else {
448
false
449
}
450
}
451
452
/// A [`SystemCondition`]-satisfying system that returns `true`
453
/// if the resource exists.
454
///
455
/// # Example
456
///
457
/// ```
458
/// # use bevy_ecs::prelude::*;
459
/// # #[derive(Resource, Default)]
460
/// # struct Counter(u8);
461
/// # let mut app = Schedule::default();
462
/// # let mut world = World::new();
463
/// app.add_systems(
464
/// // `resource_exists` will only return true if the given resource exists in the world
465
/// my_system.run_if(resource_exists::<Counter>),
466
/// );
467
///
468
/// fn my_system(mut counter: ResMut<Counter>) {
469
/// counter.0 += 1;
470
/// }
471
///
472
/// // `Counter` hasn't been added so `my_system` won't run
473
/// app.run(&mut world);
474
/// world.init_resource::<Counter>();
475
///
476
/// // `Counter` has now been added so `my_system` can run
477
/// app.run(&mut world);
478
/// assert_eq!(world.resource::<Counter>().0, 1);
479
/// ```
480
pub fn resource_exists<T>(res: Option<Res<T>>) -> bool
481
where
482
T: Resource,
483
{
484
res.is_some()
485
}
486
487
/// Generates a [`SystemCondition`]-satisfying closure that returns `true`
488
/// if the resource is equal to `value`.
489
///
490
/// # Panics
491
///
492
/// The condition will panic if the resource does not exist.
493
///
494
/// # Example
495
///
496
/// ```
497
/// # use bevy_ecs::prelude::*;
498
/// # #[derive(Resource, Default, PartialEq)]
499
/// # struct Counter(u8);
500
/// # let mut app = Schedule::default();
501
/// # let mut world = World::new();
502
/// # world.init_resource::<Counter>();
503
/// app.add_systems(
504
/// // `resource_equals` will only return true if the given resource equals the given value
505
/// my_system.run_if(resource_equals(Counter(0))),
506
/// );
507
///
508
/// fn my_system(mut counter: ResMut<Counter>) {
509
/// counter.0 += 1;
510
/// }
511
///
512
/// // `Counter` is `0` so `my_system` can run
513
/// app.run(&mut world);
514
/// assert_eq!(world.resource::<Counter>().0, 1);
515
///
516
/// // `Counter` is no longer `0` so `my_system` won't run
517
/// app.run(&mut world);
518
/// assert_eq!(world.resource::<Counter>().0, 1);
519
/// ```
520
pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool
521
where
522
T: Resource + PartialEq,
523
{
524
move |res: Res<T>| *res == value
525
}
526
527
/// Generates a [`SystemCondition`]-satisfying closure that returns `true`
528
/// if the resource exists and is equal to `value`.
529
///
530
/// The condition will return `false` if the resource does not exist.
531
///
532
/// # Example
533
///
534
/// ```
535
/// # use bevy_ecs::prelude::*;
536
/// # #[derive(Resource, Default, PartialEq)]
537
/// # struct Counter(u8);
538
/// # let mut app = Schedule::default();
539
/// # let mut world = World::new();
540
/// app.add_systems(
541
/// // `resource_exists_and_equals` will only return true
542
/// // if the given resource exists and equals the given value
543
/// my_system.run_if(resource_exists_and_equals(Counter(0))),
544
/// );
545
///
546
/// fn my_system(mut counter: ResMut<Counter>) {
547
/// counter.0 += 1;
548
/// }
549
///
550
/// // `Counter` hasn't been added so `my_system` can't run
551
/// app.run(&mut world);
552
/// world.init_resource::<Counter>();
553
///
554
/// // `Counter` is `0` so `my_system` can run
555
/// app.run(&mut world);
556
/// assert_eq!(world.resource::<Counter>().0, 1);
557
///
558
/// // `Counter` is no longer `0` so `my_system` won't run
559
/// app.run(&mut world);
560
/// assert_eq!(world.resource::<Counter>().0, 1);
561
/// ```
562
pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool
563
where
564
T: Resource + PartialEq,
565
{
566
move |res: Option<Res<T>>| match res {
567
Some(res) => *res == value,
568
None => false,
569
}
570
}
571
572
/// A [`SystemCondition`]-satisfying system that returns `true`
573
/// if the resource of the given type has been added since the condition was last checked.
574
///
575
/// # Example
576
///
577
/// ```
578
/// # use bevy_ecs::prelude::*;
579
/// # #[derive(Resource, Default)]
580
/// # struct Counter(u8);
581
/// # let mut app = Schedule::default();
582
/// # let mut world = World::new();
583
/// app.add_systems(
584
/// // `resource_added` will only return true if the
585
/// // given resource was just added
586
/// my_system.run_if(resource_added::<Counter>),
587
/// );
588
///
589
/// fn my_system(mut counter: ResMut<Counter>) {
590
/// counter.0 += 1;
591
/// }
592
///
593
/// world.init_resource::<Counter>();
594
///
595
/// // `Counter` was just added so `my_system` will run
596
/// app.run(&mut world);
597
/// assert_eq!(world.resource::<Counter>().0, 1);
598
///
599
/// // `Counter` was not just added so `my_system` will not run
600
/// app.run(&mut world);
601
/// assert_eq!(world.resource::<Counter>().0, 1);
602
/// ```
603
pub fn resource_added<T>(res: Option<Res<T>>) -> bool
604
where
605
T: Resource,
606
{
607
match res {
608
Some(res) => res.is_added(),
609
None => false,
610
}
611
}
612
613
/// A [`SystemCondition`]-satisfying system that returns `true`
614
/// if the resource of the given type has been added or mutably dereferenced
615
/// since the condition was last checked.
616
///
617
/// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
618
/// Bevy does not compare resources to their previous values.
619
///
620
/// # Panics
621
///
622
/// The condition will panic if the resource does not exist.
623
///
624
/// # Example
625
///
626
/// ```
627
/// # use bevy_ecs::prelude::*;
628
/// # #[derive(Resource, Default)]
629
/// # struct Counter(u8);
630
/// # let mut app = Schedule::default();
631
/// # let mut world = World::new();
632
/// # world.init_resource::<Counter>();
633
/// app.add_systems(
634
/// // `resource_changed` will only return true if the
635
/// // given resource was just changed (or added)
636
/// my_system.run_if(
637
/// resource_changed::<Counter>
638
/// // By default detecting changes will also trigger if the resource was
639
/// // just added, this won't work with my example so I will add a second
640
/// // condition to make sure the resource wasn't just added
641
/// .and(not(resource_added::<Counter>))
642
/// ),
643
/// );
644
///
645
/// fn my_system(mut counter: ResMut<Counter>) {
646
/// counter.0 += 1;
647
/// }
648
///
649
/// // `Counter` hasn't been changed so `my_system` won't run
650
/// app.run(&mut world);
651
/// assert_eq!(world.resource::<Counter>().0, 0);
652
///
653
/// world.resource_mut::<Counter>().0 = 50;
654
///
655
/// // `Counter` was just changed so `my_system` will run
656
/// app.run(&mut world);
657
/// assert_eq!(world.resource::<Counter>().0, 51);
658
/// ```
659
pub fn resource_changed<T>(res: Res<T>) -> bool
660
where
661
T: Resource,
662
{
663
res.is_changed()
664
}
665
666
/// A [`SystemCondition`]-satisfying system that returns `true`
667
/// if the resource of the given type has been added or mutably dereferenced since the condition
668
/// was last checked.
669
///
670
/// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
671
/// Bevy does not compare resources to their previous values.
672
///
673
/// The condition will return `false` if the resource does not exist.
674
///
675
/// # Example
676
///
677
/// ```
678
/// # use bevy_ecs::prelude::*;
679
/// # #[derive(Resource, Default)]
680
/// # struct Counter(u8);
681
/// # let mut app = Schedule::default();
682
/// # let mut world = World::new();
683
/// app.add_systems(
684
/// // `resource_exists_and_changed` will only return true if the
685
/// // given resource exists and was just changed (or added)
686
/// my_system.run_if(
687
/// resource_exists_and_changed::<Counter>
688
/// // By default detecting changes will also trigger if the resource was
689
/// // just added, this won't work with my example so I will add a second
690
/// // condition to make sure the resource wasn't just added
691
/// .and(not(resource_added::<Counter>))
692
/// ),
693
/// );
694
///
695
/// fn my_system(mut counter: ResMut<Counter>) {
696
/// counter.0 += 1;
697
/// }
698
///
699
/// // `Counter` doesn't exist so `my_system` won't run
700
/// app.run(&mut world);
701
/// world.init_resource::<Counter>();
702
///
703
/// // `Counter` hasn't been changed so `my_system` won't run
704
/// app.run(&mut world);
705
/// assert_eq!(world.resource::<Counter>().0, 0);
706
///
707
/// world.resource_mut::<Counter>().0 = 50;
708
///
709
/// // `Counter` was just changed so `my_system` will run
710
/// app.run(&mut world);
711
/// assert_eq!(world.resource::<Counter>().0, 51);
712
/// ```
713
pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool
714
where
715
T: Resource,
716
{
717
match res {
718
Some(res) => res.is_changed(),
719
None => false,
720
}
721
}
722
723
/// A [`SystemCondition`]-satisfying system that returns `true`
724
/// if the resource of the given type has been added, removed or mutably dereferenced since the condition
725
/// was last checked.
726
///
727
/// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
728
/// Bevy does not compare resources to their previous values.
729
///
730
/// The condition will return `false` if the resource does not exist.
731
///
732
/// # Example
733
///
734
/// ```
735
/// # use bevy_ecs::prelude::*;
736
/// # #[derive(Resource, Default)]
737
/// # struct Counter(u8);
738
/// # let mut app = Schedule::default();
739
/// # let mut world = World::new();
740
/// # world.init_resource::<Counter>();
741
/// app.add_systems(
742
/// // `resource_changed_or_removed` will only return true if the
743
/// // given resource was just changed or removed (or added)
744
/// my_system.run_if(
745
/// resource_changed_or_removed::<Counter>
746
/// // By default detecting changes will also trigger if the resource was
747
/// // just added, this won't work with my example so I will add a second
748
/// // condition to make sure the resource wasn't just added
749
/// .and(not(resource_added::<Counter>))
750
/// ),
751
/// );
752
///
753
/// #[derive(Resource, Default)]
754
/// struct MyResource;
755
///
756
/// // If `Counter` exists, increment it, otherwise insert `MyResource`
757
/// fn my_system(mut commands: Commands, mut counter: Option<ResMut<Counter>>) {
758
/// if let Some(mut counter) = counter {
759
/// counter.0 += 1;
760
/// } else {
761
/// commands.init_resource::<MyResource>();
762
/// }
763
/// }
764
///
765
/// // `Counter` hasn't been changed so `my_system` won't run
766
/// app.run(&mut world);
767
/// assert_eq!(world.resource::<Counter>().0, 0);
768
///
769
/// world.resource_mut::<Counter>().0 = 50;
770
///
771
/// // `Counter` was just changed so `my_system` will run
772
/// app.run(&mut world);
773
/// assert_eq!(world.resource::<Counter>().0, 51);
774
///
775
/// world.remove_resource::<Counter>();
776
///
777
/// // `Counter` was just removed so `my_system` will run
778
/// app.run(&mut world);
779
/// assert_eq!(world.contains_resource::<MyResource>(), true);
780
/// ```
781
pub fn resource_changed_or_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
782
where
783
T: Resource,
784
{
785
if let Some(value) = res {
786
*existed = true;
787
value.is_changed()
788
} else if *existed {
789
*existed = false;
790
true
791
} else {
792
false
793
}
794
}
795
796
/// A [`SystemCondition`]-satisfying system that returns `true`
797
/// if the resource of the given type has been removed since the condition was last checked.
798
///
799
/// # Example
800
///
801
/// ```
802
/// # use bevy_ecs::prelude::*;
803
/// # #[derive(Resource, Default)]
804
/// # struct Counter(u8);
805
/// # let mut app = Schedule::default();
806
/// # let mut world = World::new();
807
/// # world.init_resource::<Counter>();
808
/// app.add_systems(
809
/// // `resource_removed` will only return true if the
810
/// // given resource was just removed
811
/// my_system.run_if(resource_removed::<MyResource>),
812
/// );
813
///
814
/// #[derive(Resource, Default)]
815
/// struct MyResource;
816
///
817
/// fn my_system(mut counter: ResMut<Counter>) {
818
/// counter.0 += 1;
819
/// }
820
///
821
/// world.init_resource::<MyResource>();
822
///
823
/// // `MyResource` hasn't just been removed so `my_system` won't run
824
/// app.run(&mut world);
825
/// assert_eq!(world.resource::<Counter>().0, 0);
826
///
827
/// world.remove_resource::<MyResource>();
828
///
829
/// // `MyResource` was just removed so `my_system` will run
830
/// app.run(&mut world);
831
/// assert_eq!(world.resource::<Counter>().0, 1);
832
/// ```
833
pub fn resource_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
834
where
835
T: Resource,
836
{
837
if res.is_some() {
838
*existed = true;
839
false
840
} else if *existed {
841
*existed = false;
842
true
843
} else {
844
false
845
}
846
}
847
848
/// A [`SystemCondition`]-satisfying system that returns `true`
849
/// if there are any new events of the given type since it was last called.
850
///
851
/// # Example
852
///
853
/// ```
854
/// # use bevy_ecs::prelude::*;
855
/// # #[derive(Resource, Default)]
856
/// # struct Counter(u8);
857
/// # let mut app = Schedule::default();
858
/// # let mut world = World::new();
859
/// # world.init_resource::<Counter>();
860
/// # world.init_resource::<Messages<MyMessage>>();
861
/// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));
862
///
863
/// app.add_systems(
864
/// my_system.run_if(on_message::<MyMessage>),
865
/// );
866
///
867
/// #[derive(Message)]
868
/// struct MyMessage;
869
///
870
/// fn my_system(mut counter: ResMut<Counter>) {
871
/// counter.0 += 1;
872
/// }
873
///
874
/// // No new `MyMessage` events have been push so `my_system` won't run
875
/// app.run(&mut world);
876
/// assert_eq!(world.resource::<Counter>().0, 0);
877
///
878
/// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);
879
///
880
/// // A `MyMessage` event has been pushed so `my_system` will run
881
/// app.run(&mut world);
882
/// assert_eq!(world.resource::<Counter>().0, 1);
883
/// ```
884
#[deprecated(since = "0.17.0", note = "Use `on_message` instead.")]
885
pub fn on_event<T: Message>(reader: MessageReader<T>) -> bool {
886
on_message(reader)
887
}
888
889
/// A [`SystemCondition`]-satisfying system that returns `true`
890
/// if there are any new messages of the given type since it was last called.
891
///
892
/// # Example
893
///
894
/// ```
895
/// # use bevy_ecs::prelude::*;
896
/// # #[derive(Resource, Default)]
897
/// # struct Counter(u8);
898
/// # let mut app = Schedule::default();
899
/// # let mut world = World::new();
900
/// # world.init_resource::<Counter>();
901
/// # world.init_resource::<Messages<MyMessage>>();
902
/// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));
903
///
904
/// app.add_systems(
905
/// my_system.run_if(on_message::<MyMessage>),
906
/// );
907
///
908
/// #[derive(Message)]
909
/// struct MyMessage;
910
///
911
/// fn my_system(mut counter: ResMut<Counter>) {
912
/// counter.0 += 1;
913
/// }
914
///
915
/// // No new `MyMessage` messages have been push so `my_system` won't run
916
/// app.run(&mut world);
917
/// assert_eq!(world.resource::<Counter>().0, 0);
918
///
919
/// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);
920
///
921
/// // A `MyMessage` message has been pushed so `my_system` will run
922
/// app.run(&mut world);
923
/// assert_eq!(world.resource::<Counter>().0, 1);
924
/// ```
925
pub fn on_message<M: Message>(mut reader: MessageReader<M>) -> bool {
926
// The messages need to be consumed, so that there are no false positives on subsequent
927
// calls of the run condition. Simply checking `is_empty` would not be enough.
928
// PERF: note that `count` is efficient (not actually looping/iterating),
929
// due to Bevy having a specialized implementation for messages.
930
reader.read().count() > 0
931
}
932
933
/// A [`SystemCondition`]-satisfying system that returns `true`
934
/// if there are any entities with the given component type.
935
///
936
/// # Example
937
///
938
/// ```
939
/// # use bevy_ecs::prelude::*;
940
/// # #[derive(Resource, Default)]
941
/// # struct Counter(u8);
942
/// # let mut app = Schedule::default();
943
/// # let mut world = World::new();
944
/// # world.init_resource::<Counter>();
945
/// app.add_systems(
946
/// my_system.run_if(any_with_component::<MyComponent>),
947
/// );
948
///
949
/// #[derive(Component)]
950
/// struct MyComponent;
951
///
952
/// fn my_system(mut counter: ResMut<Counter>) {
953
/// counter.0 += 1;
954
/// }
955
///
956
/// // No entities exist yet with a `MyComponent` component so `my_system` won't run
957
/// app.run(&mut world);
958
/// assert_eq!(world.resource::<Counter>().0, 0);
959
///
960
/// world.spawn(MyComponent);
961
///
962
/// // An entities with `MyComponent` now exists so `my_system` will run
963
/// app.run(&mut world);
964
/// assert_eq!(world.resource::<Counter>().0, 1);
965
/// ```
966
pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {
967
!query.is_empty()
968
}
969
970
/// A [`SystemCondition`]-satisfying system that returns `true`
971
/// if there are any entity with a component of the given type removed.
972
pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
973
// `RemovedComponents` based on events and therefore events need to be consumed,
974
// so that there are no false positives on subsequent calls of the run condition.
975
// Simply checking `is_empty` would not be enough.
976
// PERF: note that `count` is efficient (not actually looping/iterating),
977
// due to Bevy having a specialized implementation for events.
978
removals.read().count() > 0
979
}
980
981
/// A [`SystemCondition`]-satisfying system that returns `true`
982
/// if there are any entities that match the given [`QueryFilter`].
983
pub fn any_match_filter<F: QueryFilter>(query: Query<(), F>) -> bool {
984
!query.is_empty()
985
}
986
987
/// Generates a [`SystemCondition`] that inverses the result of passed one.
988
///
989
/// # Example
990
///
991
/// ```
992
/// # use bevy_ecs::prelude::*;
993
/// # #[derive(Resource, Default)]
994
/// # struct Counter(u8);
995
/// # let mut app = Schedule::default();
996
/// # let mut world = World::new();
997
/// # world.init_resource::<Counter>();
998
/// app.add_systems(
999
/// // `not` will inverse any condition you pass in.
1000
/// // Since the condition we choose always returns true
1001
/// // this system will never run
1002
/// my_system.run_if(not(always)),
1003
/// );
1004
///
1005
/// fn my_system(mut counter: ResMut<Counter>) {
1006
/// counter.0 += 1;
1007
/// }
1008
///
1009
/// fn always() -> bool {
1010
/// true
1011
/// }
1012
///
1013
/// app.run(&mut world);
1014
/// assert_eq!(world.resource::<Counter>().0, 0);
1015
/// ```
1016
pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
1017
where
1018
TOut: core::ops::Not,
1019
T: IntoSystem<(), TOut, Marker>,
1020
{
1021
let condition = IntoSystem::into_system(condition);
1022
let name = format!("!{}", condition.name());
1023
NotSystem::new(super::NotMarker, condition, name.into())
1024
}
1025
1026
/// Generates a [`SystemCondition`] that returns true when the passed one changes.
1027
///
1028
/// The first time this is called, the passed condition is assumed to have been previously false.
1029
///
1030
/// # Example
1031
///
1032
/// ```
1033
/// # use bevy_ecs::prelude::*;
1034
/// # #[derive(Resource, Default)]
1035
/// # struct Counter(u8);
1036
/// # let mut app = Schedule::default();
1037
/// # let mut world = World::new();
1038
/// # world.init_resource::<Counter>();
1039
/// app.add_systems(
1040
/// my_system.run_if(condition_changed(resource_exists::<MyResource>)),
1041
/// );
1042
///
1043
/// #[derive(Resource)]
1044
/// struct MyResource;
1045
///
1046
/// fn my_system(mut counter: ResMut<Counter>) {
1047
/// counter.0 += 1;
1048
/// }
1049
///
1050
/// // `MyResource` is initially there, the inner condition is true, the system runs once
1051
/// world.insert_resource(MyResource);
1052
/// app.run(&mut world);
1053
/// assert_eq!(world.resource::<Counter>().0, 1);
1054
/// app.run(&mut world);
1055
/// assert_eq!(world.resource::<Counter>().0, 1);
1056
///
1057
/// // We remove `MyResource`, the inner condition is now false, the system runs one more time.
1058
/// world.remove_resource::<MyResource>();
1059
/// app.run(&mut world);
1060
/// assert_eq!(world.resource::<Counter>().0, 2);
1061
/// app.run(&mut world);
1062
/// assert_eq!(world.resource::<Counter>().0, 2);
1063
/// ```
1064
pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl SystemCondition<(), CIn>
1065
where
1066
CIn: SystemInput,
1067
C: SystemCondition<Marker, CIn>,
1068
{
1069
IntoSystem::into_system(condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {
1070
let changed = *prev != new;
1071
*prev = new;
1072
changed
1073
}))
1074
}
1075
1076
/// Generates a [`SystemCondition`] that returns true when the result of
1077
/// the passed one went from false to true since the last time this was called.
1078
///
1079
/// The first time this is called, the passed condition is assumed to have been previously false.
1080
///
1081
/// # Example
1082
///
1083
/// ```
1084
/// # use bevy_ecs::prelude::*;
1085
/// # #[derive(Resource, Default)]
1086
/// # struct Counter(u8);
1087
/// # let mut app = Schedule::default();
1088
/// # let mut world = World::new();
1089
/// # world.init_resource::<Counter>();
1090
/// app.add_systems(
1091
/// my_system.run_if(condition_changed_to(true, resource_exists::<MyResource>)),
1092
/// );
1093
///
1094
/// #[derive(Resource)]
1095
/// struct MyResource;
1096
///
1097
/// fn my_system(mut counter: ResMut<Counter>) {
1098
/// counter.0 += 1;
1099
/// }
1100
///
1101
/// // `MyResource` is initially there, the inner condition is true, the system runs once
1102
/// world.insert_resource(MyResource);
1103
/// app.run(&mut world);
1104
/// assert_eq!(world.resource::<Counter>().0, 1);
1105
/// app.run(&mut world);
1106
/// assert_eq!(world.resource::<Counter>().0, 1);
1107
///
1108
/// // We remove `MyResource`, the inner condition is now false, the system doesn't run.
1109
/// world.remove_resource::<MyResource>();
1110
/// app.run(&mut world);
1111
/// assert_eq!(world.resource::<Counter>().0, 1);
1112
///
1113
/// // We reinsert `MyResource` again, so the system will run one more time
1114
/// world.insert_resource(MyResource);
1115
/// app.run(&mut world);
1116
/// assert_eq!(world.resource::<Counter>().0, 2);
1117
/// app.run(&mut world);
1118
/// assert_eq!(world.resource::<Counter>().0, 2);
1119
/// ```
1120
pub fn condition_changed_to<Marker, CIn, C>(
1121
to: bool,
1122
condition: C,
1123
) -> impl SystemCondition<(), CIn>
1124
where
1125
CIn: SystemInput,
1126
C: SystemCondition<Marker, CIn>,
1127
{
1128
IntoSystem::into_system(condition.pipe(
1129
move |In(new): In<bool>, mut prev: Local<bool>| -> bool {
1130
let now_true = *prev != new && new == to;
1131
*prev = new;
1132
now_true
1133
},
1134
))
1135
}
1136
}
1137
1138
/// Invokes [`Not`] with the output of another system.
1139
///
1140
/// See [`common_conditions::not`] for examples.
1141
pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
1142
1143
/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.
1144
#[doc(hidden)]
1145
#[derive(Clone, Copy)]
1146
pub struct NotMarker;
1147
1148
impl<S: System<Out: Not>> Adapt<S> for NotMarker {
1149
type In = S::In;
1150
type Out = <S::Out as Not>::Output;
1151
1152
fn adapt(
1153
&mut self,
1154
input: <Self::In as SystemInput>::Inner<'_>,
1155
run_system: impl FnOnce(SystemIn<'_, S>) -> Result<S::Out, RunSystemError>,
1156
) -> Result<Self::Out, RunSystemError> {
1157
run_system(input).map(Not::not)
1158
}
1159
}
1160
1161
/// Combines the outputs of two systems using the `&&` operator.
1162
pub type And<A, B> = CombinatorSystem<AndMarker, A, B>;
1163
1164
/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.
1165
pub type Nand<A, B> = CombinatorSystem<NandMarker, A, B>;
1166
1167
/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.
1168
pub type Nor<A, B> = CombinatorSystem<NorMarker, A, B>;
1169
1170
/// Combines the outputs of two systems using the `||` operator.
1171
pub type Or<A, B> = CombinatorSystem<OrMarker, A, B>;
1172
1173
/// Combines and inverts the outputs of two systems using the `^` and `!` operators.
1174
pub type Xnor<A, B> = CombinatorSystem<XnorMarker, A, B>;
1175
1176
/// Combines the outputs of two systems using the `^` operator.
1177
pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
1178
1179
#[doc(hidden)]
1180
pub struct AndMarker;
1181
1182
impl<In, A, B> Combine<A, B> for AndMarker
1183
where
1184
for<'a> In: SystemInput<Inner<'a>: Copy>,
1185
A: System<In = In, Out = bool>,
1186
B: System<In = In, Out = bool>,
1187
{
1188
type In = In;
1189
type Out = bool;
1190
1191
fn combine<T>(
1192
input: <Self::In as SystemInput>::Inner<'_>,
1193
data: &mut T,
1194
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1195
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1196
) -> Result<Self::Out, RunSystemError> {
1197
Ok(a(input, data)? && b(input, data)?)
1198
}
1199
}
1200
1201
#[doc(hidden)]
1202
pub struct NandMarker;
1203
1204
impl<In, A, B> Combine<A, B> for NandMarker
1205
where
1206
for<'a> In: SystemInput<Inner<'a>: Copy>,
1207
A: System<In = In, Out = bool>,
1208
B: System<In = In, Out = bool>,
1209
{
1210
type In = In;
1211
type Out = bool;
1212
1213
fn combine<T>(
1214
input: <Self::In as SystemInput>::Inner<'_>,
1215
data: &mut T,
1216
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1217
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1218
) -> Result<Self::Out, RunSystemError> {
1219
Ok(!(a(input, data)? && b(input, data)?))
1220
}
1221
}
1222
1223
#[doc(hidden)]
1224
pub struct NorMarker;
1225
1226
impl<In, A, B> Combine<A, B> for NorMarker
1227
where
1228
for<'a> In: SystemInput<Inner<'a>: Copy>,
1229
A: System<In = In, Out = bool>,
1230
B: System<In = In, Out = bool>,
1231
{
1232
type In = In;
1233
type Out = bool;
1234
1235
fn combine<T>(
1236
input: <Self::In as SystemInput>::Inner<'_>,
1237
data: &mut T,
1238
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1239
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1240
) -> Result<Self::Out, RunSystemError> {
1241
Ok(!(a(input, data)? || b(input, data)?))
1242
}
1243
}
1244
1245
#[doc(hidden)]
1246
pub struct OrMarker;
1247
1248
impl<In, A, B> Combine<A, B> for OrMarker
1249
where
1250
for<'a> In: SystemInput<Inner<'a>: Copy>,
1251
A: System<In = In, Out = bool>,
1252
B: System<In = In, Out = bool>,
1253
{
1254
type In = In;
1255
type Out = bool;
1256
1257
fn combine<T>(
1258
input: <Self::In as SystemInput>::Inner<'_>,
1259
data: &mut T,
1260
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1261
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1262
) -> Result<Self::Out, RunSystemError> {
1263
Ok(a(input, data)? || b(input, data)?)
1264
}
1265
}
1266
1267
#[doc(hidden)]
1268
pub struct XnorMarker;
1269
1270
impl<In, A, B> Combine<A, B> for XnorMarker
1271
where
1272
for<'a> In: SystemInput<Inner<'a>: Copy>,
1273
A: System<In = In, Out = bool>,
1274
B: System<In = In, Out = bool>,
1275
{
1276
type In = In;
1277
type Out = bool;
1278
1279
fn combine<T>(
1280
input: <Self::In as SystemInput>::Inner<'_>,
1281
data: &mut T,
1282
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1283
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1284
) -> Result<Self::Out, RunSystemError> {
1285
Ok(!(a(input, data)? ^ b(input, data)?))
1286
}
1287
}
1288
1289
#[doc(hidden)]
1290
pub struct XorMarker;
1291
1292
impl<In, A, B> Combine<A, B> for XorMarker
1293
where
1294
for<'a> In: SystemInput<Inner<'a>: Copy>,
1295
A: System<In = In, Out = bool>,
1296
B: System<In = In, Out = bool>,
1297
{
1298
type In = In;
1299
type Out = bool;
1300
1301
fn combine<T>(
1302
input: <Self::In as SystemInput>::Inner<'_>,
1303
data: &mut T,
1304
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1305
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1306
) -> Result<Self::Out, RunSystemError> {
1307
Ok(a(input, data)? ^ b(input, data)?)
1308
}
1309
}
1310
1311
#[cfg(test)]
1312
mod tests {
1313
use super::{common_conditions::*, SystemCondition};
1314
use crate::error::{BevyError, DefaultErrorHandler, ErrorContext};
1315
use crate::{
1316
change_detection::ResMut,
1317
component::Component,
1318
message::Message,
1319
query::With,
1320
schedule::{IntoScheduleConfigs, Schedule},
1321
system::{IntoSystem, Local, Res, System},
1322
world::World,
1323
};
1324
use bevy_ecs_macros::{Resource, SystemSet};
1325
1326
#[derive(Resource, Default)]
1327
struct Counter(usize);
1328
1329
fn increment_counter(mut counter: ResMut<Counter>) {
1330
counter.0 += 1;
1331
}
1332
1333
fn double_counter(mut counter: ResMut<Counter>) {
1334
counter.0 *= 2;
1335
}
1336
1337
fn every_other_time(mut has_ran: Local<bool>) -> bool {
1338
*has_ran = !*has_ran;
1339
*has_ran
1340
}
1341
1342
#[test]
1343
fn run_condition() {
1344
let mut world = World::new();
1345
world.init_resource::<Counter>();
1346
let mut schedule = Schedule::default();
1347
1348
// Run every other cycle
1349
schedule.add_systems(increment_counter.run_if(every_other_time));
1350
1351
schedule.run(&mut world);
1352
schedule.run(&mut world);
1353
assert_eq!(world.resource::<Counter>().0, 1);
1354
schedule.run(&mut world);
1355
schedule.run(&mut world);
1356
assert_eq!(world.resource::<Counter>().0, 2);
1357
1358
// Run every other cycle opposite to the last one
1359
schedule.add_systems(increment_counter.run_if(not(every_other_time)));
1360
1361
schedule.run(&mut world);
1362
schedule.run(&mut world);
1363
assert_eq!(world.resource::<Counter>().0, 4);
1364
schedule.run(&mut world);
1365
schedule.run(&mut world);
1366
assert_eq!(world.resource::<Counter>().0, 6);
1367
}
1368
1369
#[test]
1370
fn run_condition_combinators() {
1371
let mut world = World::new();
1372
world.init_resource::<Counter>();
1373
let mut schedule = Schedule::default();
1374
1375
schedule.add_systems(
1376
(
1377
increment_counter.run_if(every_other_time.and(|| true)), // Run every odd cycle.
1378
increment_counter.run_if(every_other_time.nand(|| false)), // Always run.
1379
double_counter.run_if(every_other_time.nor(|| false)), // Run every even cycle.
1380
increment_counter.run_if(every_other_time.or(|| true)), // Always run.
1381
increment_counter.run_if(every_other_time.xnor(|| true)), // Run every odd cycle.
1382
double_counter.run_if(every_other_time.xnor(|| false)), // Run every even cycle.
1383
increment_counter.run_if(every_other_time.xor(|| false)), // Run every odd cycle.
1384
double_counter.run_if(every_other_time.xor(|| true)), // Run every even cycle.
1385
)
1386
.chain(),
1387
);
1388
1389
schedule.run(&mut world);
1390
assert_eq!(world.resource::<Counter>().0, 5);
1391
schedule.run(&mut world);
1392
assert_eq!(world.resource::<Counter>().0, 52);
1393
}
1394
1395
#[test]
1396
fn multiple_run_conditions() {
1397
let mut world = World::new();
1398
world.init_resource::<Counter>();
1399
let mut schedule = Schedule::default();
1400
1401
// Run every other cycle
1402
schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));
1403
// Never run
1404
schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));
1405
1406
schedule.run(&mut world);
1407
assert_eq!(world.resource::<Counter>().0, 1);
1408
schedule.run(&mut world);
1409
assert_eq!(world.resource::<Counter>().0, 1);
1410
}
1411
1412
#[test]
1413
fn multiple_run_conditions_is_and_operation() {
1414
let mut world = World::new();
1415
world.init_resource::<Counter>();
1416
1417
let mut schedule = Schedule::default();
1418
1419
// This should never run, if multiple run conditions worked
1420
// like an OR condition then it would always run
1421
schedule.add_systems(
1422
increment_counter
1423
.run_if(every_other_time)
1424
.run_if(not(every_other_time)),
1425
);
1426
1427
schedule.run(&mut world);
1428
assert_eq!(world.resource::<Counter>().0, 0);
1429
schedule.run(&mut world);
1430
assert_eq!(world.resource::<Counter>().0, 0);
1431
}
1432
#[derive(Component)]
1433
struct TestComponent;
1434
1435
#[derive(Message)]
1436
struct TestMessage;
1437
1438
#[derive(Resource)]
1439
struct TestResource(());
1440
1441
fn test_system() {}
1442
1443
// Ensure distributive_run_if compiles with the common conditions.
1444
#[test]
1445
fn distributive_run_if_compiles() {
1446
Schedule::default().add_systems(
1447
(test_system, test_system)
1448
.distributive_run_if(run_once)
1449
.distributive_run_if(resource_exists::<TestResource>)
1450
.distributive_run_if(resource_added::<TestResource>)
1451
.distributive_run_if(resource_changed::<TestResource>)
1452
.distributive_run_if(resource_exists_and_changed::<TestResource>)
1453
.distributive_run_if(resource_changed_or_removed::<TestResource>)
1454
.distributive_run_if(resource_removed::<TestResource>)
1455
.distributive_run_if(on_message::<TestMessage>)
1456
.distributive_run_if(any_with_component::<TestComponent>)
1457
.distributive_run_if(any_match_filter::<With<TestComponent>>)
1458
.distributive_run_if(not(run_once)),
1459
);
1460
}
1461
1462
#[test]
1463
fn run_if_error_contains_system() {
1464
let mut world = World::new();
1465
world.insert_resource(DefaultErrorHandler(my_error_handler));
1466
1467
#[derive(Resource)]
1468
struct MyResource;
1469
1470
fn condition(_res: Res<MyResource>) -> bool {
1471
true
1472
}
1473
1474
fn my_error_handler(_: BevyError, ctx: ErrorContext) {
1475
let a = IntoSystem::into_system(system_a);
1476
let b = IntoSystem::into_system(system_b);
1477
assert!(
1478
matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))
1479
);
1480
}
1481
1482
fn system_a() {}
1483
fn system_b() {}
1484
1485
let mut schedule = Schedule::default();
1486
schedule.add_systems(system_a.run_if(condition));
1487
schedule.run(&mut world);
1488
1489
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
1490
struct Set;
1491
1492
let mut schedule = Schedule::default();
1493
schedule
1494
.add_systems((system_b,).in_set(Set))
1495
.configure_sets(Set.run_if(condition));
1496
schedule.run(&mut world);
1497
}
1498
}
1499
1500