Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more granular system sets for state transition schedule ordering #13763

Merged
merged 8 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions crates/bevy_state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ pub mod prelude {
pub use crate::condition::*;
#[doc(hidden)]
pub use crate::state::{
last_transition, ComputedStates, NextState, OnEnter, OnExit, OnTransition, State, StateSet,
StateTransition, StateTransitionEvent, StateTransitionSteps, States, SubStates,
last_transition, ComputedStates, EnterSchedules, ExitSchedules, NextState, OnEnter, OnExit,
OnTransition, State, StateSet, StateTransition, StateTransitionEvent, States, SubStates,
TransitionSchedules,
};
#[doc(hidden)]
pub use crate::state_scoped::StateScoped;
Expand Down
26 changes: 16 additions & 10 deletions crates/bevy_state/src/state/freely_mutable_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,33 @@ use super::{take_next_state, transitions::*};
pub trait FreelyMutableState: States {
/// This function registers all the necessary systems to apply state changes and run transition schedules
fn register_state(schedule: &mut Schedule) {
schedule.configure_sets((
ApplyStateTransition::<Self>::default()
.in_set(StateTransitionSteps::DependentTransitions),
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
ExitSchedules::<Self>::default().in_set(StateTransitionSteps::ExitSchedules),
TransitionSchedules::<Self>::default()
.in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<Self>::default().in_set(StateTransitionSteps::EnterSchedules),
));

schedule
.add_systems(
apply_state_transition::<Self>.in_set(ApplyStateTransition::<Self>::apply()),
)
.add_systems(
last_transition::<Self>
.pipe(run_enter::<Self>)
.in_set(StateTransitionSteps::EnterSchedules),
apply_state_transition::<Self>.in_set(ApplyStateTransition::<Self>::default()),
)
.add_systems(
last_transition::<Self>
.pipe(run_exit::<Self>)
.in_set(StateTransitionSteps::ExitSchedules),
.in_set(ExitSchedules::<Self>::default()),
)
.add_systems(
last_transition::<Self>
.pipe(run_transition::<Self>)
.in_set(StateTransitionSteps::TransitionSchedules),
.in_set(TransitionSchedules::<Self>::default()),
)
.configure_sets(
ApplyStateTransition::<Self>::apply().in_set(StateTransitionSteps::RootTransitions),
.add_systems(
last_transition::<Self>
.pipe(run_enter::<Self>)
.in_set(EnterSchedules::<Self>::default()),
);
}
}
Expand Down
80 changes: 80 additions & 0 deletions crates/bevy_state/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,4 +619,84 @@ mod tests {
1
);
}

#[derive(Resource, Default, Debug)]
struct TransitionTracker(Vec<&'static str>);

MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
#[test]
fn check_transition_orders() {
let mut world = World::new();
setup_state_transitions_in_world(&mut world, None);
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SubState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<TestComputedState>>(&mut world);
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
world.insert_resource(State(SimpleState::B(true)));
world.init_resource::<State<SubState>>();
world.insert_resource(State(TestComputedState::BisTrue));
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
let mut schedules = world.remove_resource::<Schedules>().unwrap();
let mut apply_changes = Schedule::new(StateTransition);
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
SimpleState::register_state(&mut apply_changes);
SubState::register_sub_state_systems(&mut apply_changes);
TestComputedState::register_computed_state_systems(&mut apply_changes);
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
schedules.insert(apply_changes);

world.init_resource::<TransitionTracker>();
fn register_transition(string: &'static str) -> impl Fn(ResMut<TransitionTracker>) {
move |mut transitions: ResMut<TransitionTracker>| transitions.0.push(string)
}

schedules.add_systems(
StateTransition,
register_transition("simple exit").in_set(ExitSchedules::<SimpleState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("simple transition")
.in_set(TransitionSchedules::<SimpleState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("simple enter").in_set(EnterSchedules::<SimpleState>::default()),
);

schedules.add_systems(
StateTransition,
register_transition("sub exit").in_set(ExitSchedules::<SubState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("sub transition")
.in_set(TransitionSchedules::<SubState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("sub enter").in_set(EnterSchedules::<SubState>::default()),
);

schedules.add_systems(
StateTransition,
register_transition("computed exit")
.in_set(ExitSchedules::<TestComputedState>::default()),
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
);
schedules.add_systems(
StateTransition,
register_transition("computed transition")
.in_set(TransitionSchedules::<TestComputedState>::default()),
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
);
schedules.add_systems(
StateTransition,
register_transition("computed enter")
.in_set(EnterSchedules::<TestComputedState>::default()),
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
);

world.insert_resource(schedules);

world.run_schedule(StateTransition);

let transitions = &world.resource::<TransitionTracker>().0;
println!("{:?}", transitions);
assert_eq!(transitions.len(), 9);
assert_eq!(transitions[0], "simple");
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(transitions[8], "simple");
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
}
}
121 changes: 78 additions & 43 deletions crates/bevy_state/src/state/state_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use self::sealed::StateSetSealed;
use super::{
computed_states::ComputedStates, internal_apply_state_transition, last_transition, run_enter,
run_exit, run_transition, sub_states::SubStates, take_next_state, ApplyStateTransition,
NextState, State, StateTransitionEvent, StateTransitionSteps, States,
EnterSchedules, ExitSchedules, NextState, State, StateTransitionEvent, StateTransitionSteps,
States, TransitionSchedules,
};

mod sealed {
Expand Down Expand Up @@ -114,27 +115,35 @@ impl<S: InnerStateSet> StateSet for S {
internal_apply_state_transition(event, commands, current_state, new_state);
};

schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
.after(ApplyStateTransition::<S::RawState>::default()),
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
.before(ExitSchedules::<S::RawState>::default()),
TransitionSchedules::<T>::default().in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
.after(EnterSchedules::<S::RawState>::default()),
));

schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::apply()))
.add_systems(
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(StateTransitionSteps::EnterSchedules),
)
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(
last_transition::<T>
.pipe(run_exit::<T>)
.in_set(StateTransitionSteps::ExitSchedules),
.in_set(ExitSchedules::<T>::default()),
)
.add_systems(
last_transition::<T>
.pipe(run_transition::<T>)
.in_set(StateTransitionSteps::TransitionSchedules),
.in_set(TransitionSchedules::<T>::default()),
)
.configure_sets(
ApplyStateTransition::<T>::apply()
.in_set(StateTransitionSteps::DependentTransitions)
.after(ApplyStateTransition::<S::RawState>::apply()),
.add_systems(
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(EnterSchedules::<T>::default()),
);
}

Expand Down Expand Up @@ -186,27 +195,35 @@ impl<S: InnerStateSet> StateSet for S {
internal_apply_state_transition(event, commands, current_state_res, new_state);
};

schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
.after(ApplyStateTransition::<S::RawState>::default()),
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
.before(ExitSchedules::<S::RawState>::default()),
TransitionSchedules::<T>::default().in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
.after(EnterSchedules::<S::RawState>::default()),
));

schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::apply()))
.add_systems(
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(StateTransitionSteps::EnterSchedules),
)
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(
last_transition::<T>
.pipe(run_exit::<T>)
.in_set(StateTransitionSteps::ExitSchedules),
.in_set(ExitSchedules::<T>::default()),
)
.add_systems(
last_transition::<T>
.pipe(run_transition::<T>)
.in_set(StateTransitionSteps::TransitionSchedules),
.in_set(TransitionSchedules::<T>::default()),
)
.configure_sets(
ApplyStateTransition::<T>::apply()
.in_set(StateTransitionSteps::DependentTransitions)
.after(ApplyStateTransition::<S::RawState>::apply()),
.add_systems(
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(EnterSchedules::<T>::default()),
);
}
}
Expand Down Expand Up @@ -243,16 +260,25 @@ macro_rules! impl_state_set_sealed_tuples {
internal_apply_state_transition(event, commands, current_state, new_state);
};

schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::apply()))
.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()
schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
$(.after(ApplyStateTransition::<$param::RawState>::apply()))*
);
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
$(.before(ExitSchedules::<$param::RawState>::default()))*,
TransitionSchedules::<T>::default()
.in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
$(.after(EnterSchedules::<$param::RawState>::default()))*,
));

schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
}

fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
Expand Down Expand Up @@ -288,16 +314,25 @@ macro_rules! impl_state_set_sealed_tuples {
internal_apply_state_transition(event, commands, current_state_res, new_state);
};

schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::apply()))
.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()
schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
$(.after(ApplyStateTransition::<$param::RawState>::apply()))*
);
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
$(.before(ExitSchedules::<$param::RawState>::default()))*,
TransitionSchedules::<T>::default()
.in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
$(.after(EnterSchedules::<$param::RawState>::default()))*,
));

schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
}
}
};
Expand Down
48 changes: 39 additions & 9 deletions crates/bevy_state/src/state/transitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,56 @@ pub struct StateTransitionEvent<S: States> {
///
/// These system sets are run sequentially, in the order of the enum variants.
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
pub enum StateTransitionSteps {
pub(crate) enum StateTransitionSteps {
/// Parentless states apply their [`NextState<S>`].
RootTransitions,
/// States with parents apply their computation and [`NextState<S>`].
DependentTransitions,
/// Exit schedules are executed.
/// Exit schedules are executed in leaf-root order
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
ExitSchedules,
/// Transition schedules are executed.
/// Transition schedules are executed in arbitrary order.
TransitionSchedules,
/// Enter schedules are executed.
/// Enter schedules are executed in root-leaf order.
EnterSchedules,
}

/// Defines a system set to aid with dependent state ordering
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ApplyStateTransition<S: States>(PhantomData<S>);
/// Exit schedules are executed.
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
pub struct ExitSchedules<S: States>(PhantomData<S>);

impl<S: States> ApplyStateTransition<S> {
pub(crate) fn apply() -> Self {
Self(PhantomData)
impl<S: States> Default for ExitSchedules<S> {
fn default() -> Self {
Self(Default::default())
}
}

#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
/// Transition schedules are executed.
pub struct TransitionSchedules<S: States>(PhantomData<S>);

impl<S: States> Default for TransitionSchedules<S> {
fn default() -> Self {
Self(Default::default())
}
}

#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
/// Enter schedules are executed.
pub struct EnterSchedules<S: States>(PhantomData<S>);

impl<S: States> Default for EnterSchedules<S> {
fn default() -> Self {
Self(Default::default())
}
}

/// Defines a system set to aid with dependent state ordering.
MiniaczQ marked this conversation as resolved.
Show resolved Hide resolved
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ApplyStateTransition<S: States>(PhantomData<S>);

impl<S: States> Default for ApplyStateTransition<S> {
fn default() -> Self {
Self(Default::default())
}
}

Expand Down
Loading
Loading