Make initial StateTransition
run before PreStartup
(#14208)
# Objective - Fixes #14206 ## Solution - Run initial `StateTransition` as a startup schedule before `PreStartup`, instead of running it inside `Startup` as an exclusive system. Related discord discussion: https://discord.com/channels/691052431525675048/692572690833473578/1259543775668207678 ## Testing Reproduction now works correctly: ```rs use bevy::prelude::*; #[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)] enum AppState { #[default] Menu, InGame, } fn main() { App::new() .add_plugins(DefaultPlugins) .init_state::<AppState>() .add_systems(Startup, setup) .add_systems(OnEnter(AppState::Menu), enter_menu_state) .run(); } fn setup(mut next_state: ResMut<NextState<AppState>>) { next_state.set(AppState::Menu); } fn enter_menu_state() { println!("Entered menu state"); } ```  --- ## Changelog - Initial `StateTransition` runs before `PreStartup` instead of inside `Startup`.
This commit is contained in:
parent
7f3fea9a5b
commit
524fb01457
@ -1,9 +1,5 @@
|
|||||||
use bevy_app::{App, MainScheduleOrder, Plugin, PreUpdate, Startup, SubApp};
|
use bevy_app::{App, MainScheduleOrder, Plugin, PreStartup, PreUpdate, SubApp};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{event::Events, schedule::IntoSystemConfigs, world::FromWorld};
|
||||||
event::Events,
|
|
||||||
schedule::{IntoSystemConfigs, ScheduleLabel},
|
|
||||||
world::FromWorld,
|
|
||||||
};
|
|
||||||
use bevy_utils::{tracing::warn, warn_once};
|
use bevy_utils::{tracing::warn, warn_once};
|
||||||
|
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
@ -215,7 +211,8 @@ impl Plugin for StatesPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
let mut schedule = app.world_mut().resource_mut::<MainScheduleOrder>();
|
let mut schedule = app.world_mut().resource_mut::<MainScheduleOrder>();
|
||||||
schedule.insert_after(PreUpdate, StateTransition);
|
schedule.insert_after(PreUpdate, StateTransition);
|
||||||
setup_state_transitions_in_world(app.world_mut(), Some(Startup.intern()));
|
schedule.insert_startup_before(PreStartup, StateTransition);
|
||||||
|
setup_state_transitions_in_world(app.world_mut());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ pub use transitions::*;
|
|||||||
mod tests {
|
mod tests {
|
||||||
use bevy_ecs::event::EventRegistry;
|
use bevy_ecs::event::EventRegistry;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_ecs::schedule::ScheduleLabel;
|
|
||||||
use bevy_state_macros::States;
|
use bevy_state_macros::States;
|
||||||
use bevy_state_macros::SubStates;
|
use bevy_state_macros::SubStates;
|
||||||
|
|
||||||
@ -64,7 +63,7 @@ mod tests {
|
|||||||
|
|
||||||
world.insert_resource(schedules);
|
world.insert_resource(schedules);
|
||||||
|
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
world.run_schedule(StateTransition);
|
world.run_schedule(StateTransition);
|
||||||
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
||||||
@ -120,7 +119,7 @@ mod tests {
|
|||||||
|
|
||||||
world.insert_resource(schedules);
|
world.insert_resource(schedules);
|
||||||
|
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
world.run_schedule(StateTransition);
|
world.run_schedule(StateTransition);
|
||||||
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
||||||
@ -180,7 +179,7 @@ mod tests {
|
|||||||
|
|
||||||
world.insert_resource(schedules);
|
world.insert_resource(schedules);
|
||||||
|
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
world.run_schedule(StateTransition);
|
world.run_schedule(StateTransition);
|
||||||
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
||||||
@ -275,7 +274,7 @@ mod tests {
|
|||||||
|
|
||||||
world.insert_resource(schedules);
|
world.insert_resource(schedules);
|
||||||
|
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
world.run_schedule(StateTransition);
|
world.run_schedule(StateTransition);
|
||||||
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
||||||
@ -354,9 +353,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
struct Startup;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn computed_state_transitions_are_produced_correctly() {
|
fn computed_state_transitions_are_produced_correctly() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
@ -367,7 +363,7 @@ mod tests {
|
|||||||
world.init_resource::<State<SimpleState2>>();
|
world.init_resource::<State<SimpleState2>>();
|
||||||
world.init_resource::<Schedules>();
|
world.init_resource::<Schedules>();
|
||||||
|
|
||||||
setup_state_transitions_in_world(&mut world, Some(Startup.intern()));
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
let mut schedules = world
|
let mut schedules = world
|
||||||
.get_resource_mut::<Schedules>()
|
.get_resource_mut::<Schedules>()
|
||||||
@ -431,7 +427,7 @@ mod tests {
|
|||||||
|
|
||||||
world.init_resource::<ComputedStateTransitionCounter>();
|
world.init_resource::<ComputedStateTransitionCounter>();
|
||||||
|
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
|
||||||
assert_eq!(world.resource::<State<SimpleState2>>().0, SimpleState2::A1);
|
assert_eq!(world.resource::<State<SimpleState2>>().0, SimpleState2::A1);
|
||||||
@ -508,7 +504,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn same_state_transition_should_emit_event_and_not_run_schedules() {
|
fn same_state_transition_should_emit_event_and_not_run_schedules() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
|
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
|
||||||
world.init_resource::<State<SimpleState>>();
|
world.init_resource::<State<SimpleState>>();
|
||||||
let mut schedules = world.resource_mut::<Schedules>();
|
let mut schedules = world.resource_mut::<Schedules>();
|
||||||
@ -568,7 +564,7 @@ mod tests {
|
|||||||
SubState::register_sub_state_systems(&mut apply_changes);
|
SubState::register_sub_state_systems(&mut apply_changes);
|
||||||
schedules.insert(apply_changes);
|
schedules.insert(apply_changes);
|
||||||
world.insert_resource(schedules);
|
world.insert_resource(schedules);
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
world.insert_resource(NextState::Pending(SimpleState::B(true)));
|
world.insert_resource(NextState::Pending(SimpleState::B(true)));
|
||||||
world.run_schedule(StateTransition);
|
world.run_schedule(StateTransition);
|
||||||
@ -599,7 +595,7 @@ mod tests {
|
|||||||
TestComputedState::register_computed_state_systems(&mut apply_changes);
|
TestComputedState::register_computed_state_systems(&mut apply_changes);
|
||||||
schedules.insert(apply_changes);
|
schedules.insert(apply_changes);
|
||||||
world.insert_resource(schedules);
|
world.insert_resource(schedules);
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
|
|
||||||
world.insert_resource(NextState::Pending(SimpleState::B(true)));
|
world.insert_resource(NextState::Pending(SimpleState::B(true)));
|
||||||
world.run_schedule(StateTransition);
|
world.run_schedule(StateTransition);
|
||||||
@ -651,7 +647,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_transition_orders() {
|
fn check_transition_orders() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
setup_state_transitions_in_world(&mut world, None);
|
setup_state_transitions_in_world(&mut world);
|
||||||
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
|
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
|
||||||
EventRegistry::register_event::<StateTransitionEvent<SubState>>(&mut world);
|
EventRegistry::register_event::<StateTransitionEvent<SubState>>(&mut world);
|
||||||
EventRegistry::register_event::<StateTransitionEvent<TransitionTestingComputedState>>(
|
EventRegistry::register_event::<StateTransitionEvent<TransitionTestingComputedState>>(
|
||||||
|
@ -2,9 +2,7 @@ use std::{marker::PhantomData, mem};
|
|||||||
|
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
event::{Event, EventReader, EventWriter},
|
event::{Event, EventReader, EventWriter},
|
||||||
schedule::{
|
schedule::{IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet},
|
||||||
InternedScheduleLabel, IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet,
|
|
||||||
},
|
|
||||||
system::{Commands, In, ResMut},
|
system::{Commands, In, ResMut},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
@ -181,10 +179,7 @@ pub(crate) fn internal_apply_state_transition<S: States>(
|
|||||||
///
|
///
|
||||||
/// Runs automatically when using `App` to insert states, but needs to
|
/// Runs automatically when using `App` to insert states, but needs to
|
||||||
/// be added manually in other situations.
|
/// be added manually in other situations.
|
||||||
pub fn setup_state_transitions_in_world(
|
pub fn setup_state_transitions_in_world(world: &mut World) {
|
||||||
world: &mut World,
|
|
||||||
startup_label: Option<InternedScheduleLabel>,
|
|
||||||
) {
|
|
||||||
let mut schedules = world.get_resource_or_insert_with(Schedules::default);
|
let mut schedules = world.get_resource_or_insert_with(Schedules::default);
|
||||||
if schedules.contains(StateTransition) {
|
if schedules.contains(StateTransition) {
|
||||||
return;
|
return;
|
||||||
@ -200,12 +195,6 @@ pub fn setup_state_transitions_in_world(
|
|||||||
.chain(),
|
.chain(),
|
||||||
);
|
);
|
||||||
schedules.insert(schedule);
|
schedules.insert(schedule);
|
||||||
|
|
||||||
if let Some(startup) = startup_label {
|
|
||||||
schedules.add_systems(startup, |world: &mut World| {
|
|
||||||
let _ = world.try_run_schedule(StateTransition);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the latest state transition event of type `S`, if any are available.
|
/// Returns the latest state transition event of type `S`, if any are available.
|
||||||
|
Loading…
Reference in New Issue
Block a user