
# Objective - Alternative to and builds on top of #16284. - Fixes #15849. ## Solution - Rename component `StateScoped` to `DespawnOnExitState`. - Rename system `clear_state_scoped_entities` to `despawn_entities_on_exit_state`. - Add `DespawnOnEnterState` and `despawn_entities_on_enter_state` which is the `OnEnter` equivalent. > [!NOTE] > Compared to #16284, the main change is that I did the rename in such a way as to keep the terms `OnExit` and `OnEnter` together. In my own game, I was adding `VisibleOnEnterState` and `HiddenOnExitState` and when naming those, I kept the `OnExit` and `OnEnter` together. When I checked #16284 it stood out to me that the naming was a bit awkward. Putting the `State` in the middle and breaking up `OnEnter` and `OnExit` also breaks searching for those terms. ## Open questions 1. Should we split `enable_state_scoped_entities` into two functions, one for the `OnEnter` and one for the `OnExit`? I personally have zero need thus far for the `OnEnter` version, so I'd be interested in not having this enabled unless I ask for it. 2. If yes to 1., should we follow my lead in my `Visibility` state components (see below) and name these `app.enable_despawn_entities_on_enter_state()` and `app.enable_despawn_entities_on_exit_state()`, which IMO says what it does on the tin? ## Testing Ran all changed examples. ## Side note: `VisibleOnEnterState` and `HiddenOnExitState` For reference to anyone else and to help with the open questions, I'm including the code I wrote for controlling entity visibility when a state is entered/exited. <details> <summary>visibility.rs</summary> ```rust use bevy_app::prelude::*; use bevy_ecs::prelude::*; use bevy_reflect::prelude::*; use bevy_render::prelude::*; use bevy_state::{prelude::*, state::StateTransitionSteps}; use tracing::*; pub trait AppExtStates { fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self; fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self; } impl AppExtStates for App { fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self { self.main_mut() .enable_visible_entities_on_enter_state::<S>(); self } fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self { self.main_mut().enable_hidden_entities_on_exit_state::<S>(); self } } impl AppExtStates for SubApp { fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self { if !self .world() .contains_resource::<Events<StateTransitionEvent<S>>>() { let name = core::any::type_name::<S>(); warn!("Visible entities on enter state are enabled for state `{}`, but the state isn't installed in the app!", name); } // We work with [`StateTransition`] in set // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`], // because [`OnExit`] only runs for one specific variant of the state. self.add_systems( StateTransition, update_to_visible_on_enter_state::<S>.in_set(StateTransitionSteps::ExitSchedules), ) } fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self { if !self .world() .contains_resource::<Events<StateTransitionEvent<S>>>() { let name = core::any::type_name::<S>(); warn!("Hidden entities on exit state are enabled for state `{}`, but the state isn't installed in the app!", name); } // We work with [`StateTransition`] in set // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`], // because [`OnExit`] only runs for one specific variant of the state. self.add_systems( StateTransition, update_to_hidden_on_exit_state::<S>.in_set(StateTransitionSteps::ExitSchedules), ) } } #[derive(Clone, Component, Debug, Reflect)] #[reflect(Component, Debug)] pub struct VisibleOnEnterState<S: States>(pub S); #[derive(Clone, Component, Debug, Reflect)] #[reflect(Component, Debug)] pub struct HiddenOnExitState<S: States>(pub S); /// Makes entities marked with [`VisibleOnEnterState<S>`] visible when the state /// `S` is entered. pub fn update_to_visible_on_enter_state<S: States>( mut transitions: EventReader<StateTransitionEvent<S>>, mut query: Query<(&VisibleOnEnterState<S>, &mut Visibility)>, ) { // We use the latest event, because state machine internals generate at most // 1 transition event (per type) each frame. No event means no change // happened and we skip iterating all entities. let Some(transition) = transitions.read().last() else { return; }; if transition.entered == transition.exited { return; } let Some(entered) = &transition.entered else { return; }; for (binding, mut visibility) in query.iter_mut() { if binding.0 == *entered { visibility.set_if_neq(Visibility::Visible); } } } /// Makes entities marked with [`HiddenOnExitState<S>`] invisible when the state /// `S` is exited. pub fn update_to_hidden_on_exit_state<S: States>( mut transitions: EventReader<StateTransitionEvent<S>>, mut query: Query<(&HiddenOnExitState<S>, &mut Visibility)>, ) { // We use the latest event, because state machine internals generate at most // 1 transition event (per type) each frame. No event means no change // happened and we skip iterating all entities. let Some(transition) = transitions.read().last() else { return; }; if transition.entered == transition.exited { return; } let Some(exited) = &transition.exited else { return; }; for (binding, mut visibility) in query.iter_mut() { if binding.0 == *exited { visibility.set_if_neq(Visibility::Hidden); } } } ``` </details> --------- Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com> Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
97 lines
5.0 KiB
Rust
97 lines
5.0 KiB
Rust
#![no_std]
|
|
|
|
//! In Bevy, states are app-wide interdependent, finite state machines that are generally used to model the large scale structure of your program: whether a game is paused, if the player is in combat, if assets are loaded and so on.
|
|
//!
|
|
//! This module provides 3 distinct types of state, all of which implement the [`States`](state::States) trait:
|
|
//!
|
|
//! - Standard [`States`](state::States) can only be changed by manually setting the [`NextState<S>`](state::NextState) resource.
|
|
//! These states are the baseline on which the other state types are built, and can be used on
|
|
//! their own for many simple patterns. See the [states example](https://github.com/bevyengine/bevy/blob/latest/examples/state/states.rs)
|
|
//! for a simple use case.
|
|
//! - [`SubStates`](state::SubStates) are children of other states - they can be changed manually using [`NextState<S>`](state::NextState),
|
|
//! but are removed from the [`World`](bevy_ecs::prelude::World) if the source states aren't in the right state. See the [sub_states example](https://github.com/bevyengine/bevy/blob/latest/examples/state/sub_states.rs)
|
|
//! for a simple use case based on the derive macro, or read the trait docs for more complex scenarios.
|
|
//! - [`ComputedStates`](state::ComputedStates) are fully derived from other states - they provide a [`compute`](state::ComputedStates::compute) method
|
|
//! that takes in the source states and returns their derived value. They are particularly useful for situations
|
|
//! where a simplified view of the source states is necessary - such as having an `InAMenu` computed state, derived
|
|
//! from a source state that defines multiple distinct menus. See the [computed state example](https://github.com/bevyengine/bevy/blob/latest/examples/state/computed_states.rs)
|
|
//! to see usage samples for these states.
|
|
//!
|
|
//! Most of the utilities around state involve running systems during transitions between states, or
|
|
//! determining whether to run certain systems, though they can be used more directly as well. This
|
|
//! makes it easier to transition between menus, add loading screens, pause games, and more.
|
|
//!
|
|
//! Specifically, Bevy provides the following utilities:
|
|
//!
|
|
//! - 3 Transition Schedules - [`OnEnter<S>`](crate::state::OnEnter), [`OnExit<S>`](crate::state::OnExit) and [`OnTransition<S>`](crate::state::OnTransition) - which are used
|
|
//! to trigger systems specifically during matching transitions.
|
|
//! - A [`StateTransitionEvent<S>`](crate::state::StateTransitionEvent) that gets fired when a given state changes.
|
|
//! - The [`in_state<S>`](crate::condition::in_state) and [`state_changed<S>`](crate::condition::state_changed) run conditions - which are used
|
|
//! to determine whether a system should run based on the current state.
|
|
//!
|
|
//! Bevy also provides ("state-scoped entities")[`crate::state_scoped`] functionality for managing the lifetime of entities in the context of game states.
|
|
//! This, especially in combination with system scheduling, enables a flexible and expressive way to manage spawning and despawning entities.
|
|
|
|
#![cfg_attr(
|
|
any(docsrs, docsrs_dep),
|
|
expect(
|
|
internal_features,
|
|
reason = "rustdoc_internals is needed for fake_variadic"
|
|
)
|
|
)]
|
|
#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
|
|
|
|
#[cfg(feature = "std")]
|
|
extern crate std;
|
|
|
|
extern crate alloc;
|
|
|
|
// Required to make proc macros work in bevy itself.
|
|
extern crate self as bevy_state;
|
|
|
|
#[cfg(feature = "bevy_app")]
|
|
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with state installation methods
|
|
pub mod app;
|
|
/// Provides extension methods for [`Commands`](bevy_ecs::prelude::Commands).
|
|
pub mod commands;
|
|
/// 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
|
|
pub mod state;
|
|
|
|
/// Provides tools for managing the lifetime of entities based on state transitions.
|
|
pub mod state_scoped;
|
|
#[cfg(feature = "bevy_app")]
|
|
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with methods for registering
|
|
/// state-scoped events.
|
|
pub mod state_scoped_events;
|
|
|
|
#[cfg(feature = "bevy_reflect")]
|
|
/// Provides definitions for the basic traits required by the state system
|
|
pub mod reflect;
|
|
|
|
/// The state prelude.
|
|
///
|
|
/// This includes the most common types in this crate, re-exported for your convenience.
|
|
pub mod prelude {
|
|
#[cfg(feature = "bevy_app")]
|
|
#[doc(hidden)]
|
|
pub use crate::{app::AppExtStates, state_scoped_events::StateScopedEventsAppExt};
|
|
|
|
#[cfg(feature = "bevy_reflect")]
|
|
#[doc(hidden)]
|
|
pub use crate::reflect::{ReflectFreelyMutableState, ReflectState};
|
|
|
|
#[doc(hidden)]
|
|
pub use crate::{
|
|
commands::CommandsStatesExt,
|
|
condition::*,
|
|
state::{
|
|
last_transition, ComputedStates, EnterSchedules, ExitSchedules, NextState, OnEnter,
|
|
OnExit, OnTransition, State, StateSet, StateTransition, StateTransitionEvent, States,
|
|
SubStates, TransitionSchedules,
|
|
},
|
|
state_scoped::{DespawnOnEnterState, DespawnOnExitState},
|
|
};
|
|
}
|