Simplify state transitions (#13616)

# Objective

Prerequisite to #13579.
Make state transition schedule running simpler.

## Solution

- Remove `should_run_transition` which read the latest event and
fake-fire an event for the startup transitions (e.g. startup
`OnEnter()`).
- Account for startup event, by actually emitting an event when adding
states to `App`.
- Replace `should_run_transition` with `last_transition`, which is a
light wrapper over `EventReader::read().last()`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
MiniaczQ 2024-06-01 23:00:38 +02:00 committed by GitHub
parent 4b996c75ab
commit 5cb4808026
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 52 deletions

View File

@ -308,6 +308,11 @@ impl SubApp {
.add_event::<StateTransitionEvent<S>>(); .add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap(); let schedule = self.get_schedule_mut(StateTransition).unwrap();
S::register_state(schedule); S::register_state(schedule);
let state = self.world.resource::<State<S>>().get().clone();
self.world.send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
} }
self self
@ -318,12 +323,15 @@ impl SubApp {
pub fn insert_state<S: FreelyMutableState>(&mut self, state: S) -> &mut Self { pub fn insert_state<S: FreelyMutableState>(&mut self, state: S) -> &mut Self {
if !self.world.contains_resource::<State<S>>() { if !self.world.contains_resource::<State<S>>() {
setup_state_transitions_in_world(&mut self.world, Some(Startup.intern())); setup_state_transitions_in_world(&mut self.world, Some(Startup.intern()));
self.insert_resource::<State<S>>(State::new(state)) self.insert_resource::<State<S>>(State::new(state.clone()))
.init_resource::<NextState<S>>() .init_resource::<NextState<S>>()
.add_event::<StateTransitionEvent<S>>(); .add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap(); let schedule = self.get_schedule_mut(StateTransition).unwrap();
S::register_state(schedule); S::register_state(schedule);
self.world.send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
} }
self self
@ -340,6 +348,11 @@ impl SubApp {
self.add_event::<StateTransitionEvent<S>>(); self.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap(); let schedule = self.get_schedule_mut(StateTransition).unwrap();
S::register_computed_state_systems(schedule); S::register_computed_state_systems(schedule);
let state = self.world.resource::<State<S>>().get().clone();
self.world.send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
} }
self self
@ -357,6 +370,11 @@ impl SubApp {
self.add_event::<StateTransitionEvent<S>>(); self.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap(); let schedule = self.get_schedule_mut(StateTransition).unwrap();
S::register_sub_state_systems(schedule); S::register_sub_state_systems(schedule);
let state = self.world.resource::<State<S>>().get().clone();
self.world.send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
} }
self self

View File

@ -17,17 +17,17 @@ pub trait FreelyMutableState: States {
apply_state_transition::<Self>.in_set(ApplyStateTransition::<Self>::apply()), apply_state_transition::<Self>.in_set(ApplyStateTransition::<Self>::apply()),
) )
.add_systems( .add_systems(
should_run_transition::<Self, OnEnter<Self>> last_transition::<Self>
.pipe(run_enter::<Self>) .pipe(run_enter::<Self>)
.in_set(StateTransitionSteps::EnterSchedules), .in_set(StateTransitionSteps::EnterSchedules),
) )
.add_systems( .add_systems(
should_run_transition::<Self, OnExit<Self>> last_transition::<Self>
.pipe(run_exit::<Self>) .pipe(run_exit::<Self>)
.in_set(StateTransitionSteps::ExitSchedules), .in_set(StateTransitionSteps::ExitSchedules),
) )
.add_systems( .add_systems(
should_run_transition::<Self, OnTransition<Self>> last_transition::<Self>
.pipe(run_transition::<Self>) .pipe(run_transition::<Self>)
.in_set(StateTransitionSteps::TransitionSchedules), .in_set(StateTransitionSteps::TransitionSchedules),
) )

View File

@ -9,9 +9,8 @@ use self::sealed::StateSetSealed;
use super::{ use super::{
apply_state_transition, computed_states::ComputedStates, internal_apply_state_transition, apply_state_transition, computed_states::ComputedStates, internal_apply_state_transition,
run_enter, run_exit, run_transition, should_run_transition, sub_states::SubStates, last_transition, run_enter, run_exit, run_transition, sub_states::SubStates,
ApplyStateTransition, OnEnter, OnExit, OnTransition, State, StateTransitionEvent, ApplyStateTransition, State, StateTransitionEvent, StateTransitionSteps, States,
StateTransitionSteps, States,
}; };
mod sealed { mod sealed {
@ -117,17 +116,17 @@ impl<S: InnerStateSet> StateSet for S {
schedule schedule
.add_systems(system.in_set(ApplyStateTransition::<T>::apply())) .add_systems(system.in_set(ApplyStateTransition::<T>::apply()))
.add_systems( .add_systems(
should_run_transition::<T, OnEnter<T>> last_transition::<T>
.pipe(run_enter::<T>) .pipe(run_enter::<T>)
.in_set(StateTransitionSteps::EnterSchedules), .in_set(StateTransitionSteps::EnterSchedules),
) )
.add_systems( .add_systems(
should_run_transition::<T, OnExit<T>> last_transition::<T>
.pipe(run_exit::<T>) .pipe(run_exit::<T>)
.in_set(StateTransitionSteps::ExitSchedules), .in_set(StateTransitionSteps::ExitSchedules),
) )
.add_systems( .add_systems(
should_run_transition::<T, OnTransition<T>> last_transition::<T>
.pipe(run_transition::<T>) .pipe(run_transition::<T>)
.in_set(StateTransitionSteps::TransitionSchedules), .in_set(StateTransitionSteps::TransitionSchedules),
) )
@ -181,17 +180,17 @@ impl<S: InnerStateSet> StateSet for S {
apply_state_transition::<T>.in_set(StateTransitionSteps::ManualTransitions), apply_state_transition::<T>.in_set(StateTransitionSteps::ManualTransitions),
) )
.add_systems( .add_systems(
should_run_transition::<T, OnEnter<T>> last_transition::<T>
.pipe(run_enter::<T>) .pipe(run_enter::<T>)
.in_set(StateTransitionSteps::EnterSchedules), .in_set(StateTransitionSteps::EnterSchedules),
) )
.add_systems( .add_systems(
should_run_transition::<T, OnExit<T>> last_transition::<T>
.pipe(run_exit::<T>) .pipe(run_exit::<T>)
.in_set(StateTransitionSteps::ExitSchedules), .in_set(StateTransitionSteps::ExitSchedules),
) )
.add_systems( .add_systems(
should_run_transition::<T, OnTransition<T>> last_transition::<T>
.pipe(run_transition::<T>) .pipe(run_transition::<T>)
.in_set(StateTransitionSteps::TransitionSchedules), .in_set(StateTransitionSteps::TransitionSchedules),
) )
@ -232,9 +231,9 @@ macro_rules! impl_state_set_sealed_tuples {
schedule schedule
.add_systems(system.in_set(ApplyStateTransition::<T>::apply())) .add_systems(system.in_set(ApplyStateTransition::<T>::apply()))
.add_systems(should_run_transition::<T, OnEnter<T>>.pipe(run_enter::<T>).in_set(StateTransitionSteps::EnterSchedules)) .add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(StateTransitionSteps::EnterSchedules))
.add_systems(should_run_transition::<T, OnExit<T>>.pipe(run_exit::<T>).in_set(StateTransitionSteps::ExitSchedules)) .add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(StateTransitionSteps::ExitSchedules))
.add_systems(should_run_transition::<T, OnTransition<T>>.pipe(run_transition::<T>).in_set(StateTransitionSteps::TransitionSchedules)) .add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(StateTransitionSteps::TransitionSchedules))
.configure_sets( .configure_sets(
ApplyStateTransition::<T>::apply() ApplyStateTransition::<T>::apply()
.in_set(StateTransitionSteps::DependentTransitions) .in_set(StateTransitionSteps::DependentTransitions)
@ -271,9 +270,9 @@ macro_rules! impl_state_set_sealed_tuples {
schedule schedule
.add_systems(system.in_set(ApplyStateTransition::<T>::apply())) .add_systems(system.in_set(ApplyStateTransition::<T>::apply()))
.add_systems(apply_state_transition::<T>.in_set(StateTransitionSteps::ManualTransitions)) .add_systems(apply_state_transition::<T>.in_set(StateTransitionSteps::ManualTransitions))
.add_systems(should_run_transition::<T, OnEnter<T>>.pipe(run_enter::<T>).in_set(StateTransitionSteps::EnterSchedules)) .add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(StateTransitionSteps::EnterSchedules))
.add_systems(should_run_transition::<T, OnExit<T>>.pipe(run_exit::<T>).in_set(StateTransitionSteps::ExitSchedules)) .add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(StateTransitionSteps::ExitSchedules))
.add_systems(should_run_transition::<T, OnTransition<T>>.pipe(run_transition::<T>).in_set(StateTransitionSteps::TransitionSchedules)) .add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(StateTransitionSteps::TransitionSchedules))
.configure_sets( .configure_sets(
ApplyStateTransition::<T>::apply() ApplyStateTransition::<T>::apply()
.in_set(StateTransitionSteps::DependentTransitions) .in_set(StateTransitionSteps::DependentTransitions)

View File

@ -1,11 +1,11 @@
use std::{marker::PhantomData, mem, ops::DerefMut}; use std::{marker::PhantomData, mem};
use bevy_ecs::{ use bevy_ecs::{
event::{Event, EventReader, EventWriter}, event::{Event, EventReader, EventWriter},
schedule::{ schedule::{
InternedScheduleLabel, IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet, InternedScheduleLabel, IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet,
}, },
system::{Commands, In, Local, Res, ResMut}, system::{Commands, In, ResMut},
world::World, world::World,
}; };
@ -202,34 +202,18 @@ pub fn apply_state_transition<S: FreelyMutableState>(
*next_state_resource.as_mut() = NextState::<S>::Unchanged; *next_state_resource.as_mut() = NextState::<S>::Unchanged;
} }
pub(crate) fn should_run_transition<S: States, T: ScheduleLabel>( /// Returns the latest state transition event of type `S`, if any are available.
mut first: Local<bool>, pub fn last_transition<S: States>(
res: Option<Res<State<S>>>, mut reader: EventReader<StateTransitionEvent<S>>,
mut event: EventReader<StateTransitionEvent<S>>, ) -> Option<StateTransitionEvent<S>> {
) -> (Option<StateTransitionEvent<S>>, PhantomData<T>) { reader.read().last().cloned()
let first_mut = first.deref_mut();
if !*first_mut {
*first_mut = true;
if let Some(res) = res {
event.clear();
return (
Some(StateTransitionEvent {
exited: None,
entered: Some(res.get().clone()),
}),
PhantomData,
);
}
}
(event.read().last().cloned(), PhantomData)
} }
pub(crate) fn run_enter<S: States>( pub(crate) fn run_enter<S: States>(
In((transition, _)): In<(Option<StateTransitionEvent<S>>, PhantomData<OnEnter<S>>)>, transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World, world: &mut World,
) { ) {
let Some(transition) = transition else { let Some(transition) = transition.0 else {
return; return;
}; };
let Some(entered) = transition.entered else { let Some(entered) = transition.entered else {
@ -240,10 +224,10 @@ pub(crate) fn run_enter<S: States>(
} }
pub(crate) fn run_exit<S: States>( pub(crate) fn run_exit<S: States>(
In((transition, _)): In<(Option<StateTransitionEvent<S>>, PhantomData<OnExit<S>>)>, transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World, world: &mut World,
) { ) {
let Some(transition) = transition else { let Some(transition) = transition.0 else {
return; return;
}; };
let Some(exited) = transition.exited else { let Some(exited) = transition.exited else {
@ -254,13 +238,10 @@ pub(crate) fn run_exit<S: States>(
} }
pub(crate) fn run_transition<S: States>( pub(crate) fn run_transition<S: States>(
In((transition, _)): In<( transition: In<Option<StateTransitionEvent<S>>>,
Option<StateTransitionEvent<S>>,
PhantomData<OnTransition<S>>,
)>,
world: &mut World, world: &mut World,
) { ) {
let Some(transition) = transition else { let Some(transition) = transition.0 else {
return; return;
}; };
let Some(exited) = transition.exited else { let Some(exited) = transition.exited else {