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>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap();
S::register_state(schedule);
let state = self.world.resource::<State<S>>().get().clone();
self.world.send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
}
self
@ -318,12 +323,15 @@ impl SubApp {
pub fn insert_state<S: FreelyMutableState>(&mut self, state: S) -> &mut Self {
if !self.world.contains_resource::<State<S>>() {
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>>()
.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap();
S::register_state(schedule);
self.world.send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
}
self
@ -340,6 +348,11 @@ impl SubApp {
self.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap();
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
@ -357,6 +370,11 @@ impl SubApp {
self.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).unwrap();
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

View file

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

View file

@ -9,9 +9,8 @@ use self::sealed::StateSetSealed;
use super::{
apply_state_transition, computed_states::ComputedStates, internal_apply_state_transition,
run_enter, run_exit, run_transition, should_run_transition, sub_states::SubStates,
ApplyStateTransition, OnEnter, OnExit, OnTransition, State, StateTransitionEvent,
StateTransitionSteps, States,
last_transition, run_enter, run_exit, run_transition, sub_states::SubStates,
ApplyStateTransition, State, StateTransitionEvent, StateTransitionSteps, States,
};
mod sealed {
@ -117,17 +116,17 @@ impl<S: InnerStateSet> StateSet for S {
schedule
.add_systems(system.in_set(ApplyStateTransition::<T>::apply()))
.add_systems(
should_run_transition::<T, OnEnter<T>>
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(StateTransitionSteps::EnterSchedules),
)
.add_systems(
should_run_transition::<T, OnExit<T>>
last_transition::<T>
.pipe(run_exit::<T>)
.in_set(StateTransitionSteps::ExitSchedules),
)
.add_systems(
should_run_transition::<T, OnTransition<T>>
last_transition::<T>
.pipe(run_transition::<T>)
.in_set(StateTransitionSteps::TransitionSchedules),
)
@ -181,17 +180,17 @@ impl<S: InnerStateSet> StateSet for S {
apply_state_transition::<T>.in_set(StateTransitionSteps::ManualTransitions),
)
.add_systems(
should_run_transition::<T, OnEnter<T>>
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(StateTransitionSteps::EnterSchedules),
)
.add_systems(
should_run_transition::<T, OnExit<T>>
last_transition::<T>
.pipe(run_exit::<T>)
.in_set(StateTransitionSteps::ExitSchedules),
)
.add_systems(
should_run_transition::<T, OnTransition<T>>
last_transition::<T>
.pipe(run_transition::<T>)
.in_set(StateTransitionSteps::TransitionSchedules),
)
@ -232,9 +231,9 @@ macro_rules! impl_state_set_sealed_tuples {
schedule
.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(should_run_transition::<T, OnExit<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_enter::<T>).in_set(StateTransitionSteps::EnterSchedules))
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(StateTransitionSteps::ExitSchedules))
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(StateTransitionSteps::TransitionSchedules))
.configure_sets(
ApplyStateTransition::<T>::apply()
.in_set(StateTransitionSteps::DependentTransitions)
@ -271,9 +270,9 @@ macro_rules! impl_state_set_sealed_tuples {
schedule
.add_systems(system.in_set(ApplyStateTransition::<T>::apply()))
.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(should_run_transition::<T, OnExit<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_enter::<T>).in_set(StateTransitionSteps::EnterSchedules))
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(StateTransitionSteps::ExitSchedules))
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(StateTransitionSteps::TransitionSchedules))
.configure_sets(
ApplyStateTransition::<T>::apply()
.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::{
event::{Event, EventReader, EventWriter},
schedule::{
InternedScheduleLabel, IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet,
},
system::{Commands, In, Local, Res, ResMut},
system::{Commands, In, ResMut},
world::World,
};
@ -202,34 +202,18 @@ pub fn apply_state_transition<S: FreelyMutableState>(
*next_state_resource.as_mut() = NextState::<S>::Unchanged;
}
pub(crate) fn should_run_transition<S: States, T: ScheduleLabel>(
mut first: Local<bool>,
res: Option<Res<State<S>>>,
mut event: EventReader<StateTransitionEvent<S>>,
) -> (Option<StateTransitionEvent<S>>, PhantomData<T>) {
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)
/// Returns the latest state transition event of type `S`, if any are available.
pub fn last_transition<S: States>(
mut reader: EventReader<StateTransitionEvent<S>>,
) -> Option<StateTransitionEvent<S>> {
reader.read().last().cloned()
}
pub(crate) fn run_enter<S: States>(
In((transition, _)): In<(Option<StateTransitionEvent<S>>, PhantomData<OnEnter<S>>)>,
transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World,
) {
let Some(transition) = transition else {
let Some(transition) = transition.0 else {
return;
};
let Some(entered) = transition.entered else {
@ -240,10 +224,10 @@ pub(crate) fn run_enter<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,
) {
let Some(transition) = transition else {
let Some(transition) = transition.0 else {
return;
};
let Some(exited) = transition.exited else {
@ -254,13 +238,10 @@ pub(crate) fn run_exit<S: States>(
}
pub(crate) fn run_transition<S: States>(
In((transition, _)): In<(
Option<StateTransitionEvent<S>>,
PhantomData<OnTransition<S>>,
)>,
transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World,
) {
let Some(transition) = transition else {
let Some(transition) = transition.0 else {
return;
};
let Some(exited) = transition.exited else {