Path: blob/main/crates/bevy_state/src/state/state_set.rs
9374 views
use bevy_ecs::{1message::{MessageReader, MessageWriter},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, PreviousState, State, StateTransitionEvent,13StateTransitionSystems, States, 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/// [`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: MessageReader<StateTransitionEvent<S::RawState>>,98event: MessageWriter<StateTransitionEvent<T>>,99commands: Commands,100current_state: Option<ResMut<State<T>>>,101previous_state: Option<ResMut<PreviousState<T>>>,102state_set: Option<Res<State<S::RawState>>>| {103if parent_changed.is_empty() {104return;105}106parent_changed.clear();107108let new_state =109if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {110T::compute(state_set)111} else {112None113};114115internal_apply_state_transition(116event,117commands,118current_state,119previous_state,120new_state,121T::ALLOW_SAME_STATE_TRANSITIONS,122);123};124125schedule.configure_sets((126ApplyStateTransition::<T>::default()127.in_set(StateTransitionSystems::DependentTransitions)128.after(ApplyStateTransition::<S::RawState>::default()),129ExitSchedules::<T>::default()130.in_set(StateTransitionSystems::ExitSchedules)131.before(ExitSchedules::<S::RawState>::default()),132TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),133EnterSchedules::<T>::default()134.in_set(StateTransitionSystems::EnterSchedules)135.after(EnterSchedules::<S::RawState>::default()),136));137138schedule139.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))140.add_systems(141last_transition::<T>142.pipe(run_exit::<T>)143.in_set(ExitSchedules::<T>::default()),144)145.add_systems(146last_transition::<T>147.pipe(run_transition::<T>)148.in_set(TransitionSchedules::<T>::default()),149)150.add_systems(151last_transition::<T>152.pipe(run_enter::<T>)153.in_set(EnterSchedules::<T>::default()),154);155}156157fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(158schedule: &mut Schedule,159) {160// | parent changed | next state | already exists | should exist | what happens |161// | -------------- | ---------- | -------------- | ------------ | -------------------------------- |162// | false | false | false | - | - |163// | false | false | true | - | - |164// | false | true | false | false | - |165// | true | false | false | false | - |166// | true | true | false | false | - |167// | true | false | true | false | Some(current) -> None |168// | true | true | true | false | Some(current) -> None |169// | true | false | false | true | None -> Some(default) |170// | true | true | false | true | None -> Some(next) |171// | true | true | true | true | Some(current) -> Some(next) |172// | false | true | true | true | Some(current) -> Some(next) |173// | true | false | true | true | Some(current) -> Some(current) |174175let apply_state_transition =176|mut parent_changed: MessageReader<StateTransitionEvent<S::RawState>>,177event: MessageWriter<StateTransitionEvent<T>>,178commands: Commands,179current_state_res: Option<ResMut<State<T>>>,180previous_state: Option<ResMut<PreviousState<T>>>,181next_state_res: Option<ResMut<NextState<T>>>,182state_set: Option<Res<State<S::RawState>>>| {183let parent_changed = parent_changed.read().last().is_some();184let next_state = take_next_state(next_state_res);185186if !parent_changed && next_state.is_none() {187return;188}189190let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();191192let initial_state = if parent_changed {193if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {194T::should_exist(state_set)195} else {196None197}198} else {199current_state.clone()200};201let same_state_enforced = next_state202.as_ref()203.map(|(_, same_state_enforced)| same_state_enforced)204.cloned()205.unwrap_or_default();206207let new_state = initial_state.map(|x| {208next_state209.map(|(next, _)| next)210.or(current_state)211.unwrap_or(x)212});213214internal_apply_state_transition(215event,216commands,217current_state_res,218previous_state,219new_state,220same_state_enforced,221);222};223224schedule.configure_sets((225ApplyStateTransition::<T>::default()226.in_set(StateTransitionSystems::DependentTransitions)227.after(ApplyStateTransition::<S::RawState>::default()),228ExitSchedules::<T>::default()229.in_set(StateTransitionSystems::ExitSchedules)230.before(ExitSchedules::<S::RawState>::default()),231TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),232EnterSchedules::<T>::default()233.in_set(StateTransitionSystems::EnterSchedules)234.after(EnterSchedules::<S::RawState>::default()),235));236237schedule238.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))239.add_systems(240last_transition::<T>241.pipe(run_exit::<T>)242.in_set(ExitSchedules::<T>::default()),243)244.add_systems(245last_transition::<T>246.pipe(run_transition::<T>)247.in_set(TransitionSchedules::<T>::default()),248)249.add_systems(250last_transition::<T>251.pipe(run_enter::<T>)252.in_set(EnterSchedules::<T>::default()),253);254}255}256257macro_rules! impl_state_set_sealed_tuples {258($(#[$meta:meta])* $(($param: ident, $val: ident, $evt: ident)), *) => {259$(#[$meta])*260impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}261262$(#[$meta])*263impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {264265const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;266267268fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(269schedule: &mut Schedule,270) {271let apply_state_transition =272|($(mut $evt),*,): ($(MessageReader<StateTransitionEvent<$param::RawState>>),*,),273message: MessageWriter<StateTransitionEvent<T>>,274commands: Commands,275current_state: Option<ResMut<State<T>>>,276previous_state: Option<ResMut<PreviousState<T>>>,277($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {278if ($($evt.is_empty())&&*) {279return;280}281$($evt.clear();)*282283let new_state = if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {284T::compute(($($val),*, ))285} else {286None287};288289internal_apply_state_transition(message, commands, current_state, previous_state, new_state, false);290};291292schedule.configure_sets((293ApplyStateTransition::<T>::default()294.in_set(StateTransitionSystems::DependentTransitions)295$(.after(ApplyStateTransition::<$param::RawState>::default()))*,296ExitSchedules::<T>::default()297.in_set(StateTransitionSystems::ExitSchedules)298$(.before(ExitSchedules::<$param::RawState>::default()))*,299TransitionSchedules::<T>::default()300.in_set(StateTransitionSystems::TransitionSchedules),301EnterSchedules::<T>::default()302.in_set(StateTransitionSystems::EnterSchedules)303$(.after(EnterSchedules::<$param::RawState>::default()))*,304));305306schedule307.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))308.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))309.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))310.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));311}312313fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(314schedule: &mut Schedule,315) {316let apply_state_transition =317|($(mut $evt),*,): ($(MessageReader<StateTransitionEvent<$param::RawState>>),*,),318message: MessageWriter<StateTransitionEvent<T>>,319commands: Commands,320current_state_res: Option<ResMut<State<T>>>,321previous_state: Option<ResMut<PreviousState<T>>>,322next_state_res: Option<ResMut<NextState<T>>>,323($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {324let parent_changed = ($($evt.read().last().is_some())||*);325let next_state = take_next_state(next_state_res);326327if !parent_changed && next_state.is_none() {328return;329}330331let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();332333let initial_state = if parent_changed {334if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {335T::should_exist(($($val),*, ))336} else {337None338}339} else {340current_state.clone()341};342343let same_state_enforced = next_state344.as_ref()345.map(|(_, same_state_enforced)| same_state_enforced)346.cloned()347.unwrap_or_default();348349let new_state = initial_state.map(|x| {350next_state351.map(|(next, _)| next)352.or(current_state)353.unwrap_or(x)354});355356internal_apply_state_transition(message, commands, current_state_res, previous_state, new_state, same_state_enforced);357};358359schedule.configure_sets((360ApplyStateTransition::<T>::default()361.in_set(StateTransitionSystems::DependentTransitions)362$(.after(ApplyStateTransition::<$param::RawState>::default()))*,363ExitSchedules::<T>::default()364.in_set(StateTransitionSystems::ExitSchedules)365$(.before(ExitSchedules::<$param::RawState>::default()))*,366TransitionSchedules::<T>::default()367.in_set(StateTransitionSystems::TransitionSchedules),368EnterSchedules::<T>::default()369.in_set(StateTransitionSystems::EnterSchedules)370$(.after(EnterSchedules::<$param::RawState>::default()))*,371));372373schedule374.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))375.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))376.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))377.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));378}379}380};381}382383all_tuples!(384#[doc(fake_variadic)]385impl_state_set_sealed_tuples,3861,38715,388S,389s,390ereader391);392393394