Path: blob/main/crates/bevy_ecs/src/observer/condition.rs
9328 views
//! Run conditions for observers.1//!2//! This module provides the types needed to add run conditions to observers,3//! allowing them to conditionally execute based on world state.45use alloc::{boxed::Box, vec::Vec};6use core::marker::PhantomData;78use crate::{9bundle::Bundle,10event::Event,11schedule::{BoxedCondition, SystemCondition},12system::{IntoObserverSystem, IntoSystem},13world::{unsafe_world_cell::UnsafeWorldCell, World},14};1516/// Stores a boxed condition system for an observer.17pub(crate) struct ObserverCondition {18condition: BoxedCondition,19}2021impl ObserverCondition {22pub(crate) fn new<M>(condition: impl SystemCondition<M>) -> Self {23Self {24condition: Box::new(IntoSystem::into_system(condition)),25}26}2728pub(crate) fn from_boxed(condition: BoxedCondition) -> Self {29Self { condition }30}3132pub(crate) fn initialize(&mut self, world: &mut World) {33self.condition.initialize(world);34}3536/// # Safety37/// - The condition must be initialized.38/// - The world cell must have valid access for the condition's read-only parameters.39pub(crate) unsafe fn check(&mut self, world: UnsafeWorldCell) -> bool {40// SAFETY: Caller ensures world is valid and condition is initialized.41// Conditions are read-only systems, so they won't cause aliasing issues.42unsafe { self.condition.run_unsafe((), world) }.unwrap_or(false)43}44}4546#[doc(hidden)]47pub struct ObserverWithConditionMarker;4849/// An observer system with run conditions that preserves event type information.50///51/// This type is returned by [`ObserverSystemExt::run_if`](super::ObserverSystemExt::run_if)52/// and allows `entity.observe(system.run_if(cond))` to work with compile-time53/// verification that the event implements [`EntityEvent`](crate::event::EntityEvent).54pub struct ObserverWithCondition<E: Event, B: Bundle, M, S: IntoObserverSystem<E, B, M>> {55pub(crate) system: S,56pub(crate) conditions: Vec<BoxedCondition>,57pub(crate) _marker: PhantomData<fn() -> (E, B, M)>,58}5960impl<E: Event, B: Bundle, M, S: IntoObserverSystem<E, B, M>> ObserverWithCondition<E, B, M, S> {61/// Adds another run condition to this observer.62///63/// All conditions must return `true` for the observer to run (AND semantics).64///65/// **Note:** Chained `.run_if()` calls do **not** short-circuit — all conditions66/// run every time to maintain correct change detection ticks. If you need67/// short-circuit behavior, use `.run_if(a.and(b))`, but be aware this may cause68/// stale `Changed<T>` detection if the second condition is frequently skipped.69///70/// # Example71///72/// ```73/// # use bevy_ecs::prelude::*;74/// # #[derive(Event)]75/// # struct MyEvent;76/// # #[derive(Resource)]77/// # struct CondA(bool);78/// # #[derive(Resource)]79/// # struct CondB(bool);80/// # fn on_event(_: On<MyEvent>) {}81/// # let mut world = World::new();82/// # world.insert_resource(CondA(true));83/// # world.insert_resource(CondB(true));84/// world.add_observer(85/// on_event86/// .run_if(|a: Res<CondA>| a.0)87/// .run_if(|b: Res<CondB>| b.0)88/// );89/// ```90pub fn run_if<C, CM>(mut self, condition: C) -> Self91where92C: SystemCondition<CM>,93{94self.conditions95.push(Box::new(IntoSystem::into_system(condition)));96self97}9899pub(crate) fn take_conditions(self) -> (S, Vec<ObserverCondition>) {100let conditions = self101.conditions102.into_iter()103.map(ObserverCondition::from_boxed)104.collect();105(self.system, conditions)106}107}108109110