diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 1b4cfdb791..64333bfc98 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -11,10 +11,9 @@ keywords = ["bevy"] [features] trace = [] bevy_debug_stepping = [] -default = ["bevy_reflect", "bevy_state"] +default = ["bevy_reflect"] bevy_reflect = ["dep:bevy_reflect", "bevy_ecs/bevy_reflect"] serialize = ["bevy_ecs/serde"] -bevy_state = ["dep:bevy_state"] [dependencies] # bevy @@ -23,7 +22,6 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev", default-features = fa bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev", optional = true } bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.14.0-dev" } -bevy_state = { path = "../bevy_state", optional = true, version = "0.14.0-dev" } # other serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 176c5caf60..b3cf89c308 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -10,8 +10,6 @@ use bevy_ecs::{ schedule::{ScheduleBuildSettings, ScheduleLabel}, system::SystemId, }; -#[cfg(feature = "bevy_state")] -use bevy_state::{prelude::*, state::FreelyMutableState}; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; use bevy_utils::{tracing::debug, HashMap}; @@ -266,59 +264,6 @@ impl App { self.sub_apps.iter().any(|s| s.is_building_plugins()) } - #[cfg(feature = "bevy_state")] - /// Initializes a [`State`] with standard starting values. - /// - /// This method is idempotent: it has no effect when called again using the same generic type. - /// - /// Adds [`State`] and [`NextState`] resources, and enables use of the [`OnEnter`], [`OnTransition`] and [`OnExit`] schedules. - /// These schedules are triggered before [`Update`](crate::Update) and at startup. - /// - /// If you would like to control how other systems run based on the current state, you can - /// emulate this behavior using the [`in_state`] [`Condition`]. - /// - /// Note that you can also apply state transitions at other points in the schedule - /// by triggering the [`StateTransition`](`bevy_ecs::schedule::StateTransition`) schedule manually. - pub fn init_state(&mut self) -> &mut Self { - self.main_mut().init_state::(); - self - } - - #[cfg(feature = "bevy_state")] - /// Inserts a specific [`State`] to the current [`App`] and overrides any [`State`] previously - /// added of the same type. - /// - /// Adds [`State`] and [`NextState`] resources, and enables use of the [`OnEnter`], [`OnTransition`] and [`OnExit`] schedules. - /// These schedules are triggered before [`Update`](crate::Update) and at startup. - /// - /// If you would like to control how other systems run based on the current state, you can - /// emulate this behavior using the [`in_state`] [`Condition`]. - /// - /// Note that you can also apply state transitions at other points in the schedule - /// by triggering the [`StateTransition`](`bevy_ecs::schedule::StateTransition`) schedule manually. - pub fn insert_state(&mut self, state: S) -> &mut Self { - self.main_mut().insert_state::(state); - self - } - - #[cfg(feature = "bevy_state")] - /// Sets up a type implementing [`ComputedStates`]. - /// - /// This method is idempotent: it has no effect when called again using the same generic type. - pub fn add_computed_state(&mut self) -> &mut Self { - self.main_mut().add_computed_state::(); - self - } - - #[cfg(feature = "bevy_state")] - /// Sets up a type implementing [`SubStates`]. - /// - /// This method is idempotent: it has no effect when called again using the same generic type. - pub fn add_sub_state(&mut self) -> &mut Self { - self.main_mut().add_sub_state::(); - self - } - /// Adds one or more systems to the given schedule in this app's [`Schedules`]. /// /// # Examples diff --git a/crates/bevy_app/src/main_schedule.rs b/crates/bevy_app/src/main_schedule.rs index ade996cc6f..651708b363 100644 --- a/crates/bevy_app/src/main_schedule.rs +++ b/crates/bevy_app/src/main_schedule.rs @@ -4,8 +4,6 @@ use bevy_ecs::{ system::{Local, Resource}, world::{Mut, World}, }; -#[cfg(feature = "bevy_state")] -use bevy_state::state::StateTransition; /// The schedule that contains the app logic that is evaluated each tick of [`App::update()`]. /// @@ -175,8 +173,6 @@ impl Default for MainScheduleOrder { labels: vec![ First.intern(), PreUpdate.intern(), - #[cfg(feature = "bevy_state")] - StateTransition.intern(), RunFixedMainLoop.intern(), Update.intern(), SpawnScene.intern(), diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index aece290d39..472490e398 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -1,15 +1,10 @@ -use crate::{App, InternedAppLabel, Plugin, Plugins, PluginsState, Startup}; +use crate::{App, InternedAppLabel, Plugin, Plugins, PluginsState}; use bevy_ecs::{ event::EventRegistry, prelude::*, schedule::{InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel}, system::SystemId, }; -#[cfg(feature = "bevy_state")] -use bevy_state::{ - prelude::*, - state::{setup_state_transitions_in_world, FreelyMutableState}, -}; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -298,88 +293,6 @@ impl SubApp { self } - #[cfg(feature = "bevy_state")] - /// See [`App::init_state`]. - pub fn init_state(&mut self) -> &mut Self { - if !self.world.contains_resource::>() { - setup_state_transitions_in_world(&mut self.world, Some(Startup.intern())); - self.init_resource::>() - .init_resource::>() - .add_event::>(); - let schedule = self.get_schedule_mut(StateTransition).unwrap(); - S::register_state(schedule); - let state = self.world.resource::>().get().clone(); - self.world.send_event(StateTransitionEvent { - exited: None, - entered: Some(state), - }); - } - - self - } - - #[cfg(feature = "bevy_state")] - /// See [`App::insert_state`]. - pub fn insert_state(&mut self, state: S) -> &mut Self { - if !self.world.contains_resource::>() { - setup_state_transitions_in_world(&mut self.world, Some(Startup.intern())); - self.insert_resource::>(State::new(state.clone())) - .init_resource::>() - .add_event::>(); - let schedule = self.get_schedule_mut(StateTransition).unwrap(); - S::register_state(schedule); - self.world.send_event(StateTransitionEvent { - exited: None, - entered: Some(state), - }); - } - - self - } - - #[cfg(feature = "bevy_state")] - /// See [`App::add_computed_state`]. - pub fn add_computed_state(&mut self) -> &mut Self { - if !self - .world - .contains_resource::>>() - { - setup_state_transitions_in_world(&mut self.world, Some(Startup.intern())); - self.add_event::>(); - let schedule = self.get_schedule_mut(StateTransition).unwrap(); - S::register_computed_state_systems(schedule); - let state = self.world.resource::>().get().clone(); - self.world.send_event(StateTransitionEvent { - exited: None, - entered: Some(state), - }); - } - - self - } - - #[cfg(feature = "bevy_state")] - /// See [`App::add_sub_state`]. - pub fn add_sub_state(&mut self) -> &mut Self { - if !self - .world - .contains_resource::>>() - { - setup_state_transitions_in_world(&mut self.world, Some(Startup.intern())); - self.init_resource::>(); - self.add_event::>(); - let schedule = self.get_schedule_mut(StateTransition).unwrap(); - S::register_sub_state_systems(schedule); - let state = self.world.resource::>().get().clone(); - self.world.send_event(StateTransitionEvent { - exited: None, - entered: Some(state), - }); - } - - self - } - /// See [`App::add_event`]. pub fn add_event(&mut self) -> &mut Self where diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index e6cc7b3da2..8b4b91483e 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -183,7 +183,7 @@ bevy_dev_tools = ["dep:bevy_dev_tools"] ios_simulator = ["bevy_pbr?/ios_simulator", "bevy_render?/ios_simulator"] # Enable built in global state machines -bevy_state = ["dep:bevy_state", "bevy_app/bevy_state"] +bevy_state = ["dep:bevy_state"] [dependencies] # bevy diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index 026d41cc09..6920ea7fa0 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -138,6 +138,11 @@ impl PluginGroup for DefaultPlugins { group = group.add(bevy_gizmos::GizmoPlugin); } + #[cfg(feature = "bevy_state")] + { + group = group.add(bevy_state::app::StatesPlugin); + } + #[cfg(feature = "bevy_dev_tools")] { group = group.add(bevy_dev_tools::DevToolsPlugin); diff --git a/crates/bevy_state/Cargo.toml b/crates/bevy_state/Cargo.toml index 009d283eae..6919f9f0cb 100644 --- a/crates/bevy_state/Cargo.toml +++ b/crates/bevy_state/Cargo.toml @@ -12,14 +12,16 @@ categories = ["game-engines", "data-structures"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["bevy_reflect"] - +default = ["bevy_reflect", "bevy_app"] +bevy_reflect = ["dep:bevy_reflect", "bevy_ecs/bevy_reflect"] +bevy_app = ["dep:bevy_app"] [dependencies] bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" } bevy_state_macros = { path = "macros", version = "0.14.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev", optional = true } +bevy_app = { path = "../bevy_app", version = "0.14.0-dev", optional = true } [lints] workspace = true diff --git a/crates/bevy_state/src/app.rs b/crates/bevy_state/src/app.rs new file mode 100644 index 0000000000..47c4f08405 --- /dev/null +++ b/crates/bevy_state/src/app.rs @@ -0,0 +1,155 @@ +use bevy_app::{App, MainScheduleOrder, Plugin, PreUpdate, Startup, SubApp}; +use bevy_ecs::{event::Events, schedule::ScheduleLabel, world::FromWorld}; + +use crate::state::{ + setup_state_transitions_in_world, ComputedStates, FreelyMutableState, NextState, State, + StateTransition, StateTransitionEvent, SubStates, +}; + +/// State installation methods for [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp). +pub trait AppExtStates { + /// Initializes a [`State`] with standard starting values. + /// + /// This method is idempotent: it has no effect when called again using the same generic type. + /// + /// Adds [`State`] and [`NextState`] resources, and enables use of the [`OnEnter`], [`OnTransition`] and [`OnExit`] schedules. + /// These schedules are triggered before [`Update`](crate::Update) and at startup. + /// + /// If you would like to control how other systems run based on the current state, you can + /// emulate this behavior using the [`in_state`] [`Condition`]. + /// + /// Note that you can also apply state transitions at other points in the schedule + /// by triggering the [`StateTransition`](`bevy_ecs::schedule::StateTransition`) schedule manually. + fn init_state(&mut self) -> &mut Self; + + /// Inserts a specific [`State`] to the current [`App`] and overrides any [`State`] previously + /// added of the same type. + /// + /// Adds [`State`] and [`NextState`] resources, and enables use of the [`OnEnter`], [`OnTransition`] and [`OnExit`] schedules. + /// These schedules are triggered before [`Update`](crate::Update) and at startup. + /// + /// If you would like to control how other systems run based on the current state, you can + /// emulate this behavior using the [`in_state`] [`Condition`]. + /// + /// Note that you can also apply state transitions at other points in the schedule + /// by triggering the [`StateTransition`](`bevy_ecs::schedule::StateTransition`) schedule manually. + fn insert_state(&mut self, state: S) -> &mut Self; + + /// Sets up a type implementing [`ComputedStates`]. + /// + /// This method is idempotent: it has no effect when called again using the same generic type. + fn add_computed_state(&mut self) -> &mut Self; + + /// Sets up a type implementing [`SubStates`]. + /// + /// This method is idempotent: it has no effect when called again using the same generic type. + fn add_sub_state(&mut self) -> &mut Self; +} + +impl AppExtStates for SubApp { + fn init_state(&mut self) -> &mut Self { + if !self.world().contains_resource::>() { + setup_state_transitions_in_world(self.world_mut(), Some(Startup.intern())); + self.init_resource::>() + .init_resource::>() + .add_event::>(); + let schedule = self.get_schedule_mut(StateTransition).unwrap(); + S::register_state(schedule); + let state = self.world().resource::>().get().clone(); + self.world_mut().send_event(StateTransitionEvent { + exited: None, + entered: Some(state), + }); + } + + self + } + + fn insert_state(&mut self, state: S) -> &mut Self { + if !self.world().contains_resource::>() { + setup_state_transitions_in_world(self.world_mut(), Some(Startup.intern())); + self.insert_resource::>(State::new(state.clone())) + .init_resource::>() + .add_event::>(); + let schedule = self.get_schedule_mut(StateTransition).unwrap(); + S::register_state(schedule); + self.world_mut().send_event(StateTransitionEvent { + exited: None, + entered: Some(state), + }); + } + + self + } + + fn add_computed_state(&mut self) -> &mut Self { + if !self + .world() + .contains_resource::>>() + { + setup_state_transitions_in_world(self.world_mut(), Some(Startup.intern())); + self.add_event::>(); + let schedule = self.get_schedule_mut(StateTransition).unwrap(); + S::register_computed_state_systems(schedule); + let state = self.world().resource::>().get().clone(); + self.world_mut().send_event(StateTransitionEvent { + exited: None, + entered: Some(state), + }); + } + + self + } + + fn add_sub_state(&mut self) -> &mut Self { + if !self + .world() + .contains_resource::>>() + { + setup_state_transitions_in_world(self.world_mut(), Some(Startup.intern())); + self.init_resource::>(); + self.add_event::>(); + let schedule = self.get_schedule_mut(StateTransition).unwrap(); + S::register_sub_state_systems(schedule); + let state = self.world().resource::>().get().clone(); + self.world_mut().send_event(StateTransitionEvent { + exited: None, + entered: Some(state), + }); + } + + self + } +} + +impl AppExtStates for App { + fn init_state(&mut self) -> &mut Self { + self.main_mut().init_state::(); + self + } + + fn insert_state(&mut self, state: S) -> &mut Self { + self.main_mut().insert_state::(state); + self + } + + fn add_computed_state(&mut self) -> &mut Self { + self.main_mut().add_computed_state::(); + self + } + + fn add_sub_state(&mut self) -> &mut Self { + self.main_mut().add_sub_state::(); + self + } +} + +/// Registers the [`StateTransition`] schedule in the [`MainScheduleOrder`] to enable state processing. +pub struct StatesPlugin; + +impl Plugin for StatesPlugin { + fn build(&self, app: &mut App) { + let mut schedule = app.world_mut().resource_mut::(); + schedule.insert_after(PreUpdate, StateTransition); + } +} diff --git a/crates/bevy_state/src/lib.rs b/crates/bevy_state/src/lib.rs index 8555564e84..408ee9963b 100644 --- a/crates/bevy_state/src/lib.rs +++ b/crates/bevy_state/src/lib.rs @@ -27,6 +27,9 @@ //! - The [`in_state`](crate::condition::in_state) and [`state_changed`](crate::condition::state_changed) run conditions - which are used //! to determine whether a system should run based on the current state. +#[cfg(feature = "bevy_app")] +/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with state installation methods +pub mod app; /// Provides definitions for the runtime conditions that interact with the state system pub mod condition; /// Provides definitions for the basic traits required by the state system @@ -34,6 +37,9 @@ pub mod state; /// Most commonly used re-exported types. pub mod prelude { + #[cfg(feature = "bevy_app")] + #[doc(hidden)] + pub use crate::app::AppExtStates; #[doc(hidden)] pub use crate::condition::*; #[doc(hidden)]