Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/observer/condition.rs
9328 views
1
//! Run conditions for observers.
2
//!
3
//! This module provides the types needed to add run conditions to observers,
4
//! allowing them to conditionally execute based on world state.
5
6
use alloc::{boxed::Box, vec::Vec};
7
use core::marker::PhantomData;
8
9
use crate::{
10
bundle::Bundle,
11
event::Event,
12
schedule::{BoxedCondition, SystemCondition},
13
system::{IntoObserverSystem, IntoSystem},
14
world::{unsafe_world_cell::UnsafeWorldCell, World},
15
};
16
17
/// Stores a boxed condition system for an observer.
18
pub(crate) struct ObserverCondition {
19
condition: BoxedCondition,
20
}
21
22
impl ObserverCondition {
23
pub(crate) fn new<M>(condition: impl SystemCondition<M>) -> Self {
24
Self {
25
condition: Box::new(IntoSystem::into_system(condition)),
26
}
27
}
28
29
pub(crate) fn from_boxed(condition: BoxedCondition) -> Self {
30
Self { condition }
31
}
32
33
pub(crate) fn initialize(&mut self, world: &mut World) {
34
self.condition.initialize(world);
35
}
36
37
/// # Safety
38
/// - The condition must be initialized.
39
/// - The world cell must have valid access for the condition's read-only parameters.
40
pub(crate) unsafe fn check(&mut self, world: UnsafeWorldCell) -> bool {
41
// SAFETY: Caller ensures world is valid and condition is initialized.
42
// Conditions are read-only systems, so they won't cause aliasing issues.
43
unsafe { self.condition.run_unsafe((), world) }.unwrap_or(false)
44
}
45
}
46
47
#[doc(hidden)]
48
pub struct ObserverWithConditionMarker;
49
50
/// An observer system with run conditions that preserves event type information.
51
///
52
/// This type is returned by [`ObserverSystemExt::run_if`](super::ObserverSystemExt::run_if)
53
/// and allows `entity.observe(system.run_if(cond))` to work with compile-time
54
/// verification that the event implements [`EntityEvent`](crate::event::EntityEvent).
55
pub struct ObserverWithCondition<E: Event, B: Bundle, M, S: IntoObserverSystem<E, B, M>> {
56
pub(crate) system: S,
57
pub(crate) conditions: Vec<BoxedCondition>,
58
pub(crate) _marker: PhantomData<fn() -> (E, B, M)>,
59
}
60
61
impl<E: Event, B: Bundle, M, S: IntoObserverSystem<E, B, M>> ObserverWithCondition<E, B, M, S> {
62
/// Adds another run condition to this observer.
63
///
64
/// All conditions must return `true` for the observer to run (AND semantics).
65
///
66
/// **Note:** Chained `.run_if()` calls do **not** short-circuit — all conditions
67
/// run every time to maintain correct change detection ticks. If you need
68
/// short-circuit behavior, use `.run_if(a.and(b))`, but be aware this may cause
69
/// stale `Changed<T>` detection if the second condition is frequently skipped.
70
///
71
/// # Example
72
///
73
/// ```
74
/// # use bevy_ecs::prelude::*;
75
/// # #[derive(Event)]
76
/// # struct MyEvent;
77
/// # #[derive(Resource)]
78
/// # struct CondA(bool);
79
/// # #[derive(Resource)]
80
/// # struct CondB(bool);
81
/// # fn on_event(_: On<MyEvent>) {}
82
/// # let mut world = World::new();
83
/// # world.insert_resource(CondA(true));
84
/// # world.insert_resource(CondB(true));
85
/// world.add_observer(
86
/// on_event
87
/// .run_if(|a: Res<CondA>| a.0)
88
/// .run_if(|b: Res<CondB>| b.0)
89
/// );
90
/// ```
91
pub fn run_if<C, CM>(mut self, condition: C) -> Self
92
where
93
C: SystemCondition<CM>,
94
{
95
self.conditions
96
.push(Box::new(IntoSystem::into_system(condition)));
97
self
98
}
99
100
pub(crate) fn take_conditions(self) -> (S, Vec<ObserverCondition>) {
101
let conditions = self
102
.conditions
103
.into_iter()
104
.map(ObserverCondition::from_boxed)
105
.collect();
106
(self.system, conditions)
107
}
108
}
109
110