From 9c9c9465b6c51c9ddbc73c293855db46944d51a7 Mon Sep 17 00:00:00 2001 From: MiniaczQ Date: Mon, 3 Jun 2024 16:27:09 +0200 Subject: [PATCH 01/19] state bound module --- crates/bevy_state/Cargo.toml | 4 +- crates/bevy_state/src/lib.rs | 10 +++- crates/bevy_state/src/state/states.rs | 8 +-- crates/bevy_state/src/state/transitions.rs | 11 ++++ crates/bevy_state/src/state_bound.rs | 65 ++++++++++++++++++++++ 5 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 crates/bevy_state/src/state_bound.rs diff --git a/crates/bevy_state/Cargo.toml b/crates/bevy_state/Cargo.toml index 6919f9f0cb19e..39e8623e861ab 100644 --- a/crates/bevy_state/Cargo.toml +++ b/crates/bevy_state/Cargo.toml @@ -12,9 +12,10 @@ 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", "bevy_app"] +default = ["bevy_reflect", "bevy_app", "bevy_hierarchy"] bevy_reflect = ["dep:bevy_reflect", "bevy_ecs/bevy_reflect"] bevy_app = ["dep:bevy_app"] +bevy_hierarchy = ["dep:bevy_hierarchy"] [dependencies] bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" } @@ -22,6 +23,7 @@ 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 } +bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.14.0-dev", optional = true } [lints] workspace = true diff --git a/crates/bevy_state/src/lib.rs b/crates/bevy_state/src/lib.rs index 408ee9963b12e..2fc55ae3d35cb 100644 --- a/crates/bevy_state/src/lib.rs +++ b/crates/bevy_state/src/lib.rs @@ -34,6 +34,9 @@ pub mod app; pub mod condition; /// Provides definitions for the basic traits required by the state system pub mod state; +#[cfg(feature = "bevy_hierarchy")] +/// Provides [`StateBound`] and [`clear_state_bound_entities`] for managing lifetime of entities. +pub mod state_bound; /// Most commonly used re-exported types. pub mod prelude { @@ -44,7 +47,10 @@ pub mod prelude { pub use crate::condition::*; #[doc(hidden)] pub use crate::state::{ - ComputedStates, NextState, OnEnter, OnExit, OnTransition, State, StateSet, StateTransition, - StateTransitionEvent, States, SubStates, + log_transitions, ComputedStates, NextState, OnEnter, OnExit, OnTransition, State, StateSet, + StateTransition, StateTransitionEvent, States, SubStates, }; + #[cfg(feature = "bevy_hierarchy")] + #[doc(hidden)] + pub use crate::state_bound::*; } diff --git a/crates/bevy_state/src/state/states.rs b/crates/bevy_state/src/state/states.rs index 6f2be17cd5759..cde092e68a9f1 100644 --- a/crates/bevy_state/src/state/states.rs +++ b/crates/bevy_state/src/state/states.rs @@ -27,10 +27,10 @@ use std::hash::Hash; /// /// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] /// enum GameState { -/// #[default] -/// MainMenu, -/// SettingsMenu, -/// InGame, +/// #[default] +/// MainMenu, +/// SettingsMenu, +/// InGame, /// } /// /// fn handle_escape_pressed(mut next_state: ResMut>) { diff --git a/crates/bevy_state/src/state/transitions.rs b/crates/bevy_state/src/state/transitions.rs index b816b4ad173a3..e3a861b99f15b 100644 --- a/crates/bevy_state/src/state/transitions.rs +++ b/crates/bevy_state/src/state/transitions.rs @@ -8,6 +8,7 @@ use bevy_ecs::{ system::{Commands, In, ResMut}, world::World, }; +use bevy_utils::tracing::info; use super::{resources::State, states::States}; @@ -206,3 +207,13 @@ pub(crate) fn run_transition( let _ = world.try_run_schedule(OnTransition { exited, entered }); } + +/// Logs state transitions into console. +pub fn log_transitions(mut transitions: EventReader>) { + for transition in transitions.read() { + info!( + "Transition: {:?} => {:?}", + transition.exited, transition.entered + ); + } +} diff --git a/crates/bevy_state/src/state_bound.rs b/crates/bevy_state/src/state_bound.rs new file mode 100644 index 0000000000000..6fe9b8526f5a0 --- /dev/null +++ b/crates/bevy_state/src/state_bound.rs @@ -0,0 +1,65 @@ +use bevy_ecs::{ + component::Component, + entity::Entity, + system::{Commands, Query}, +}; +use bevy_hierarchy::DespawnRecursiveExt; + +use crate::state::States; + +/// Entities marked with this component will be removed +/// when the provided value no longer matches the world state. +/// +/// To enable this feature, register the [`clear_state_bound_entities`] +/// system for selected states. +/// +/// ``` +/// use bevy_state::prelude::*; +/// use bevy_ecs::prelude::IntoSystemConfigs; +/// use bevy_ecs::system::ResMut; +/// +/// +/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] +/// enum GameState { +/// #[default] +/// MainMenu, +/// SettingsMenu, +/// InGame, +/// } +/// +/// # #[derive(Component)] +/// # struct Player; +/// +/// fn spawn_player(mut commands: Commands) { +/// commands.spawn(( +/// StateBound(GameState::InGame), +/// Player +/// )); +/// } +/// +/// # struct AppMock; +/// # impl AppMock { +/// # fn add_systems(&mut self, schedule: S, systems: impl IntoSystemConfigs) {} +/// # } +/// # struct Update; +/// # let mut app = AppMock; +/// +/// app.add_systems(Update, clear_state_bound_entities::); +/// app.add_systems(OnEnter(GameState::InGame), spawn_player); +/// ``` +#[derive(Component)] +pub struct StateBound(pub S); + +/// Removes entities marked with [`StateBound`] +/// when their state no longer matches the world state. +pub fn clear_state_bound_entities( + state: S, +) -> impl Fn(Commands, Query<(Entity, &StateBound)>) { + move |mut commands, query| { + for (entity, bound) in &query { + if bound.0 == state { + commands.entity(entity).despawn_recursive(); + } + } + } +} From 0ad7eb73003b9c919a38c3d8780842371c484150 Mon Sep 17 00:00:00 2001 From: MiniaczQ Date: Mon, 3 Jun 2024 16:30:19 +0200 Subject: [PATCH 02/19] use moved features in examples --- examples/state/computed_states.rs | 43 +++++-------------------------- examples/state/state.rs | 13 +--------- examples/state/sub_states.rs | 27 +------------------ 3 files changed, 9 insertions(+), 74 deletions(-) diff --git a/examples/state/computed_states.rs b/examples/state/computed_states.rs index 77f99ad0c27ea..a1d62a0132f97 100644 --- a/examples/state/computed_states.rs +++ b/examples/state/computed_states.rs @@ -219,7 +219,13 @@ fn main() { OnExit(Tutorial::PauseInstructions), clear_state_bound_entities(Tutorial::PauseInstructions), ) - .add_systems(Update, log_transitions) + .add_systems( + Update, + ( + log_transitions::, + log_transitions::, + ), + ) .run(); } @@ -277,22 +283,6 @@ fn menu( } } -#[derive(Component)] -struct StateBound(S); - -fn clear_state_bound_entities( - state: S, -) -> impl Fn(Commands, Query<(Entity, &StateBound)>) { - info!("Clearing entities for {state:?}"); - move |mut commands, query| { - for (entity, bound) in &query { - if bound.0 == state { - commands.entity(entity).despawn_recursive(); - } - } - } -} - fn toggle_pause( input: Res>, current_state: Res>, @@ -329,25 +319,6 @@ fn quit_to_menu(input: Res>, mut next_state: ResMut>, - mut tutorial_transitions: EventReader>, -) { - for transition in transitions.read() { - info!( - "transition: {:?} => {:?}", - transition.exited, transition.entered - ); - } - for transition in tutorial_transitions.read() { - info!( - "tutorial transition: {:?} => {:?}", - transition.exited, transition.entered - ); - } -} - mod ui { use crate::*; diff --git a/examples/state/state.rs b/examples/state/state.rs index d695e82e4a75b..776f913543941 100644 --- a/examples/state/state.rs +++ b/examples/state/state.rs @@ -25,7 +25,7 @@ fn main() { Update, (movement, change_color).run_if(in_state(AppState::InGame)), ) - .add_systems(Update, log_transitions) + .add_systems(Update, log_transitions::) .run(); } @@ -163,14 +163,3 @@ fn change_color(time: Res