Path: blob/main/crates/bevy_state/src/state/resources.rs
9374 views
use core::ops::Deref;12use bevy_ecs::{3change_detection::DetectChangesMut,4resource::Resource,5system::ResMut,6world::{FromWorld, World},7};89use super::{freely_mutable_state::FreelyMutableState, states::States};1011#[cfg(feature = "bevy_reflect")]12use bevy_ecs::prelude::ReflectResource;1314#[cfg(feature = "bevy_reflect")]15use bevy_reflect::prelude::ReflectDefault;1617/// A finite-state machine whose transitions have associated schedules18/// ([`OnEnter(state)`](crate::state::OnEnter) and [`OnExit(state)`](crate::state::OnExit)).19///20/// The current state value can be accessed through this resource. To *change* the state,21/// queue a transition in the [`NextState<S>`] resource, and it will be applied during the22/// [`StateTransition`](crate::state::StateTransition) schedule - which by default runs after `PreUpdate`.23///24/// You can also manually trigger the [`StateTransition`](crate::state::StateTransition) schedule to apply the changes25/// at an arbitrary time.26///27/// The starting state is defined via the [`Default`] implementation for `S`.28///29/// ```30/// use bevy_state::prelude::*;31/// use bevy_ecs::prelude::*;32/// use bevy_state_macros::States;33///34/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]35/// enum GameState {36/// #[default]37/// MainMenu,38/// SettingsMenu,39/// InGame,40/// }41///42/// fn game_logic(game_state: Res<State<GameState>>) {43/// match game_state.get() {44/// GameState::InGame => {45/// // Run game logic here...46/// },47/// _ => {},48/// }49/// }50/// ```51#[derive(Resource, Debug)]52#[cfg_attr(53feature = "bevy_reflect",54derive(bevy_reflect::Reflect),55reflect(Resource, Debug, PartialEq)56)]57pub struct State<S: States>(pub(crate) S);5859impl<S: States> State<S> {60/// Creates a new state with a specific value.61///62/// To change the state use [`NextState<S>`] rather than using this to modify the `State<S>`.63pub fn new(state: S) -> Self {64Self(state)65}6667/// Get the current state.68pub fn get(&self) -> &S {69&self.070}71}7273impl<S: States + FromWorld> FromWorld for State<S> {74fn from_world(world: &mut World) -> Self {75Self(S::from_world(world))76}77}7879impl<S: States> PartialEq<S> for State<S> {80fn eq(&self, other: &S) -> bool {81self.get() == other82}83}8485impl<S: States> Deref for State<S> {86type Target = S;8788fn deref(&self) -> &Self::Target {89self.get()90}91}9293/// The previous state of [`State<S>`].94///95/// This resource holds the state value that was active immediately **before** the96/// most recent state transition. It is primarily useful for logic that runs97/// during state exit or transition schedules ([`OnExit`](crate::state::OnExit), [`OnTransition`](crate::state::OnTransition)).98///99/// It is inserted into the world only after the first state transition occurs. It will100/// remain present even if the primary state is removed (e.g., when a101/// [`SubStates`](crate::state::SubStates) or [`ComputedStates`](crate::state::ComputedStates) instance ceases to exist).102///103/// Use `Option<Res<PreviousState<S>>>` to access it, as it will not exist104/// before the first transition.105///106/// ```107/// use bevy_state::prelude::*;108/// use bevy_ecs::prelude::*;109/// use bevy_state_macros::States;110///111/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]112/// enum GameState {113/// #[default]114/// MainMenu,115/// InGame,116/// }117///118/// // This system might run in an OnExit schedule119/// fn log_previous_state(previous_state: Option<Res<PreviousState<GameState>>>) {120/// if let Some(previous) = previous_state {121/// // If this system is in OnExit(InGame), the previous state is what we122/// // were in before InGame.123/// println!("Transitioned from: {:?}", previous.get());124/// }125/// }126/// ```127#[derive(Resource, Debug, Clone, PartialEq, Eq)]128#[cfg_attr(129feature = "bevy_reflect",130derive(bevy_reflect::Reflect),131reflect(Resource, Debug, PartialEq)132)]133pub struct PreviousState<S: States>(pub(crate) S);134135impl<S: States> PreviousState<S> {136/// Get the previous state.137pub fn get(&self) -> &S {138&self.0139}140}141142impl<S: States> Deref for PreviousState<S> {143type Target = S;144145fn deref(&self) -> &Self::Target {146&self.0147}148}149150/// The next state of [`State<S>`].151///152/// This can be fetched as a resource and used to queue state transitions.153/// To queue a transition, call [`NextState::set`] or mutate the value to [`NextState::Pending`] directly.154///155/// Note that these transitions can be overridden by other systems:156/// only the actual value of this resource during the [`StateTransition`](crate::state::StateTransition) schedule matters.157///158/// ```159/// use bevy_state::prelude::*;160/// use bevy_ecs::prelude::*;161///162/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]163/// enum GameState {164/// #[default]165/// MainMenu,166/// SettingsMenu,167/// InGame,168/// }169///170/// fn start_game(mut next_game_state: ResMut<NextState<GameState>>) {171/// next_game_state.set(GameState::InGame);172/// }173/// ```174#[derive(Resource, Debug, Default, Clone)]175#[cfg_attr(176feature = "bevy_reflect",177derive(bevy_reflect::Reflect),178reflect(Resource, Default, Debug)179)]180pub enum NextState<S: FreelyMutableState> {181/// No state transition is pending182#[default]183Unchanged,184/// There is a pending transition for state `S`185Pending(S),186/// There is a pending transition for state `S`187///188/// This will not trigger state transitions schedules if the target state is the same as the current one.189PendingIfNeq(S),190}191192impl<S: FreelyMutableState> NextState<S> {193/// Tentatively set a pending state transition to `Some(state)`.194///195/// This will run the state transition schedules [`OnEnter`](crate::state::OnEnter) and [`OnExit`](crate::state::OnExit).196/// If you want to skip those schedules for the same where we are transitioning to the same state, use [`set_if_neq`](Self::set_if_neq) instead.197pub fn set(&mut self, state: S) {198*self = Self::Pending(state);199}200201/// Tentatively set a pending state transition to `Some(state)`.202///203/// Like [`set`](Self::set), but will not run any state transition schedules if the target state is the same as the current one.204/// If [`set`](Self::set) has already been called in the same frame with the same state, the transition schedules will be run anyways.205pub fn set_if_neq(&mut self, state: S) {206if !matches!(self, Self::Pending(s) if s == &state) {207*self = Self::PendingIfNeq(state);208}209}210211/// Remove any pending changes to [`State<S>`]212pub fn reset(&mut self) {213*self = Self::Unchanged;214}215}216217pub(crate) fn take_next_state<S: FreelyMutableState>(218next_state: Option<ResMut<NextState<S>>>,219) -> Option<(S, bool)> {220let mut next_state = next_state?;221222match core::mem::take(next_state.bypass_change_detection()) {223NextState::Pending(x) => {224next_state.set_changed();225Some((x, true))226}227NextState::PendingIfNeq(x) => {228next_state.set_changed();229Some((x, false))230}231NextState::Unchanged => None,232}233}234235236