Path: blob/main/crates/bevy_state/src/state/state_set.rs
6596 views
use bevy_ecs::{1event::{EventReader, EventWriter},2schedule::{IntoScheduleConfigs, Schedule},3system::{Commands, IntoSystem, Res, ResMut},4};5use variadics_please::all_tuples;67use self::sealed::StateSetSealed;89use super::{10computed_states::ComputedStates, internal_apply_state_transition, last_transition, run_enter,11run_exit, run_transition, sub_states::SubStates, take_next_state, ApplyStateTransition,12EnterSchedules, ExitSchedules, NextState, State, StateTransitionEvent, StateTransitionSystems,13States, TransitionSchedules,14};1516mod sealed {17/// Sealed trait used to prevent external implementations of [`StateSet`](super::StateSet).18pub trait StateSetSealed {}19}2021/// A [`States`] type or tuple of types which implement [`States`].22///23/// This trait is used to allow implementors of [`States`], as well24/// as tuples containing exclusively implementors of [`States`], to25/// be used as [`ComputedStates::SourceStates`].26///27/// It is sealed, and auto implemented for all [`States`] types and28/// tuples containing them.29pub trait StateSet: StateSetSealed {30/// The total [`DEPENDENCY_DEPTH`](`States::DEPENDENCY_DEPTH`) of all31/// the states that are part of this [`StateSet`], added together.32///33/// Used to de-duplicate computed state executions and prevent cyclic34/// computed states.35const SET_DEPENDENCY_DEPTH: usize;3637/// Sets up the systems needed to compute `T` whenever any `State` in this38/// `StateSet` is changed.39fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(40schedule: &mut Schedule,41);4243/// Sets up the systems needed to compute whether `T` exists whenever any `State` in this44/// `StateSet` is changed.45fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(46schedule: &mut Schedule,47);48}4950/// The `InnerStateSet` trait is used to isolate [`ComputedStates`] & [`SubStates`] from51/// needing to wrap all state dependencies in an [`Option<S>`].52///53/// Some [`ComputedStates`]'s might need to exist in different states based on the existence54/// of other states. So we needed the ability to use[`Option<S>`] when appropriate.55///56/// The isolation works because it is implemented for both S & [`Option<S>`], and has the `RawState` associated type57/// that allows it to know what the resource in the world should be. We can then essentially "unwrap" it in our58/// `StateSet` implementation - and the behavior of that unwrapping will depend on the arguments expected by the59/// the [`ComputedStates`] & [`SubStates]`.60trait InnerStateSet: Sized {61type RawState: States;6263const DEPENDENCY_DEPTH: usize;6465fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self>;66}6768impl<S: States> InnerStateSet for S {69type RawState = Self;7071const DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;7273fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self> {74wrapped.map(|v| v.0.clone())75}76}7778impl<S: States> InnerStateSet for Option<S> {79type RawState = S;8081const DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;8283fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self> {84Some(wrapped.map(|v| v.0.clone()))85}86}8788impl<S: InnerStateSet> StateSetSealed for S {}8990impl<S: InnerStateSet> StateSet for S {91const SET_DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;9293fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(94schedule: &mut Schedule,95) {96let apply_state_transition =97|mut parent_changed: EventReader<StateTransitionEvent<S::RawState>>,98event: EventWriter<StateTransitionEvent<T>>,99commands: Commands,100current_state: Option<ResMut<State<T>>>,101state_set: Option<Res<State<S::RawState>>>| {102if parent_changed.is_empty() {103return;104}105parent_changed.clear();106107let new_state =108if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {109T::compute(state_set)110} else {111None112};113114internal_apply_state_transition(event, commands, current_state, new_state);115};116117schedule.configure_sets((118ApplyStateTransition::<T>::default()119.in_set(StateTransitionSystems::DependentTransitions)120.after(ApplyStateTransition::<S::RawState>::default()),121ExitSchedules::<T>::default()122.in_set(StateTransitionSystems::ExitSchedules)123.before(ExitSchedules::<S::RawState>::default()),124TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),125EnterSchedules::<T>::default()126.in_set(StateTransitionSystems::EnterSchedules)127.after(EnterSchedules::<S::RawState>::default()),128));129130schedule131.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))132.add_systems(133last_transition::<T>134.pipe(run_exit::<T>)135.in_set(ExitSchedules::<T>::default()),136)137.add_systems(138last_transition::<T>139.pipe(run_transition::<T>)140.in_set(TransitionSchedules::<T>::default()),141)142.add_systems(143last_transition::<T>144.pipe(run_enter::<T>)145.in_set(EnterSchedules::<T>::default()),146);147}148149fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(150schedule: &mut Schedule,151) {152// | parent changed | next state | already exists | should exist | what happens |153// | -------------- | ---------- | -------------- | ------------ | -------------------------------- |154// | false | false | false | - | - |155// | false | false | true | - | - |156// | false | true | false | false | - |157// | true | false | false | false | - |158// | true | true | false | false | - |159// | true | false | true | false | Some(current) -> None |160// | true | true | true | false | Some(current) -> None |161// | true | false | false | true | None -> Some(default) |162// | true | true | false | true | None -> Some(next) |163// | true | true | true | true | Some(current) -> Some(next) |164// | false | true | true | true | Some(current) -> Some(next) |165// | true | false | true | true | Some(current) -> Some(current) |166167let apply_state_transition =168|mut parent_changed: EventReader<StateTransitionEvent<S::RawState>>,169event: EventWriter<StateTransitionEvent<T>>,170commands: Commands,171current_state_res: Option<ResMut<State<T>>>,172next_state_res: Option<ResMut<NextState<T>>>,173state_set: Option<Res<State<S::RawState>>>| {174let parent_changed = parent_changed.read().last().is_some();175let next_state = take_next_state(next_state_res);176177if !parent_changed && next_state.is_none() {178return;179}180181let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();182183let initial_state = if parent_changed {184if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {185T::should_exist(state_set)186} else {187None188}189} else {190current_state.clone()191};192let new_state = initial_state.map(|x| next_state.or(current_state).unwrap_or(x));193194internal_apply_state_transition(event, commands, current_state_res, new_state);195};196197schedule.configure_sets((198ApplyStateTransition::<T>::default()199.in_set(StateTransitionSystems::DependentTransitions)200.after(ApplyStateTransition::<S::RawState>::default()),201ExitSchedules::<T>::default()202.in_set(StateTransitionSystems::ExitSchedules)203.before(ExitSchedules::<S::RawState>::default()),204TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),205EnterSchedules::<T>::default()206.in_set(StateTransitionSystems::EnterSchedules)207.after(EnterSchedules::<S::RawState>::default()),208));209210schedule211.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))212.add_systems(213last_transition::<T>214.pipe(run_exit::<T>)215.in_set(ExitSchedules::<T>::default()),216)217.add_systems(218last_transition::<T>219.pipe(run_transition::<T>)220.in_set(TransitionSchedules::<T>::default()),221)222.add_systems(223last_transition::<T>224.pipe(run_enter::<T>)225.in_set(EnterSchedules::<T>::default()),226);227}228}229230macro_rules! impl_state_set_sealed_tuples {231($(#[$meta:meta])* $(($param: ident, $val: ident, $evt: ident)), *) => {232$(#[$meta])*233impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}234235$(#[$meta])*236impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {237238const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;239240241fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(242schedule: &mut Schedule,243) {244let apply_state_transition =245|($(mut $evt),*,): ($(EventReader<StateTransitionEvent<$param::RawState>>),*,),246event: EventWriter<StateTransitionEvent<T>>,247commands: Commands,248current_state: Option<ResMut<State<T>>>,249($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {250if ($($evt.is_empty())&&*) {251return;252}253$($evt.clear();)*254255let new_state = if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {256T::compute(($($val),*, ))257} else {258None259};260261internal_apply_state_transition(event, commands, current_state, new_state);262};263264schedule.configure_sets((265ApplyStateTransition::<T>::default()266.in_set(StateTransitionSystems::DependentTransitions)267$(.after(ApplyStateTransition::<$param::RawState>::default()))*,268ExitSchedules::<T>::default()269.in_set(StateTransitionSystems::ExitSchedules)270$(.before(ExitSchedules::<$param::RawState>::default()))*,271TransitionSchedules::<T>::default()272.in_set(StateTransitionSystems::TransitionSchedules),273EnterSchedules::<T>::default()274.in_set(StateTransitionSystems::EnterSchedules)275$(.after(EnterSchedules::<$param::RawState>::default()))*,276));277278schedule279.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))280.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))281.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))282.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));283}284285fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(286schedule: &mut Schedule,287) {288let apply_state_transition =289|($(mut $evt),*,): ($(EventReader<StateTransitionEvent<$param::RawState>>),*,),290event: EventWriter<StateTransitionEvent<T>>,291commands: Commands,292current_state_res: Option<ResMut<State<T>>>,293next_state_res: Option<ResMut<NextState<T>>>,294($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {295let parent_changed = ($($evt.read().last().is_some())||*);296let next_state = take_next_state(next_state_res);297298if !parent_changed && next_state.is_none() {299return;300}301302let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();303304let initial_state = if parent_changed {305if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {306T::should_exist(($($val),*, ))307} else {308None309}310} else {311current_state.clone()312};313let new_state = initial_state.map(|x| next_state.or(current_state).unwrap_or(x));314315internal_apply_state_transition(event, commands, current_state_res, new_state);316};317318schedule.configure_sets((319ApplyStateTransition::<T>::default()320.in_set(StateTransitionSystems::DependentTransitions)321$(.after(ApplyStateTransition::<$param::RawState>::default()))*,322ExitSchedules::<T>::default()323.in_set(StateTransitionSystems::ExitSchedules)324$(.before(ExitSchedules::<$param::RawState>::default()))*,325TransitionSchedules::<T>::default()326.in_set(StateTransitionSystems::TransitionSchedules),327EnterSchedules::<T>::default()328.in_set(StateTransitionSystems::EnterSchedules)329$(.after(EnterSchedules::<$param::RawState>::default()))*,330));331332schedule333.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))334.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))335.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))336.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));337}338}339};340}341342all_tuples!(343#[doc(fake_variadic)]344impl_state_set_sealed_tuples,3451,34615,347S,348s,349ereader350);351352353