diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index d70669be5a123..9ffc7a7c2305e 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -313,7 +313,7 @@ impl App { } /// Adds [`State`] and [`NextState`] resources, [`OnEnter`] and [`OnExit`] schedules - /// for each state variant, an instance of [`apply_state_transition::`] in + /// for each state variant (if they don't already exist), an instance of [`apply_state_transition::`] in /// [`CoreSet::StateTransitions`] so that transitions happen before [`CoreSet::Update`] and /// a instance of [`run_enter_schedule::`] in [`CoreSet::StateTransitions`] with a /// [`run_once`](`run_once_condition`) condition to run the on enter schedule of the @@ -355,11 +355,14 @@ impl App { .run_if(in_state(variant)), ); } - // These are different for loops to avoid conflicting access to self for variant in S::variants() { - self.add_schedule(OnEnter(variant.clone()), Schedule::new()); - self.add_schedule(OnExit(variant), Schedule::new()); + if self.get_schedule(OnEnter(variant.clone())).is_none() { + self.add_schedule(OnEnter(variant.clone()), Schedule::new()); + } + if self.get_schedule(OnExit(variant.clone())).is_none() { + self.add_schedule(OnExit(variant), Schedule::new()); + } } self @@ -390,7 +393,9 @@ impl App { if let Some(schedule) = schedules.get_mut(&*schedule_label) { schedule.add_system(system); } else { - panic!("Schedule {schedule_label:?} does not exist.") + let mut schedule = Schedule::new(); + schedule.add_system(system); + schedules.insert(schedule_label, schedule); } } else if let Some(default_schedule) = schedules.get_mut(&*self.default_schedule_label) { default_schedule.add_system(system); @@ -1014,7 +1019,12 @@ pub struct AppExit; #[cfg(test)] mod tests { - use crate::{App, Plugin}; + use bevy_ecs::{ + schedule::{OnEnter, States}, + system::Commands, + }; + + use crate::{App, IntoSystemAppConfig, IntoSystemAppConfigs, Plugin}; struct PluginA; impl Plugin for PluginA { @@ -1068,4 +1078,57 @@ mod tests { } App::new().add_plugin(PluginRun); } + + #[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)] + enum AppState { + #[default] + MainMenu, + } + fn bar(mut commands: Commands) { + commands.spawn_empty(); + } + + fn foo(mut commands: Commands) { + commands.spawn_empty(); + } + + #[test] + fn add_system_should_create_schedule_if_it_does_not_exist() { + let mut app = App::new(); + app.add_system(foo.in_schedule(OnEnter(AppState::MainMenu))) + .add_state::(); + + app.world.run_schedule(OnEnter(AppState::MainMenu)); + assert_eq!(app.world.entities().len(), 1); + } + + #[test] + fn add_system_should_create_schedule_if_it_does_not_exist2() { + let mut app = App::new(); + app.add_state::() + .add_system(foo.in_schedule(OnEnter(AppState::MainMenu))); + + app.world.run_schedule(OnEnter(AppState::MainMenu)); + assert_eq!(app.world.entities().len(), 1); + } + + #[test] + fn add_systems_should_create_schedule_if_it_does_not_exist() { + let mut app = App::new(); + app.add_state::() + .add_systems((foo, bar).in_schedule(OnEnter(AppState::MainMenu))); + + app.world.run_schedule(OnEnter(AppState::MainMenu)); + assert_eq!(app.world.entities().len(), 2); + } + + #[test] + fn add_systems_should_create_schedule_if_it_does_not_exist2() { + let mut app = App::new(); + app.add_systems((foo, bar).in_schedule(OnEnter(AppState::MainMenu))) + .add_state::(); + + app.world.run_schedule(OnEnter(AppState::MainMenu)); + assert_eq!(app.world.entities().len(), 2); + } } diff --git a/crates/bevy_app/src/config.rs b/crates/bevy_app/src/config.rs index 3a5f30a50453b..fb3a3fde1cd92 100644 --- a/crates/bevy_app/src/config.rs +++ b/crates/bevy_app/src/config.rs @@ -190,7 +190,10 @@ pub trait IntoSystemAppConfigs: Sized { /// Adds the systems to the provided `schedule`. /// - /// If a schedule is not specified, they will be added to the [`App`]'s default schedule. + /// If a schedule with specified label does not exist, it will be created. + /// + /// If a schedule with the specified label does not exist, an empty one will be created. + /// /// /// [`App`]: crate::App ///