bevy_state/state/
resources.rs

1use core::ops::Deref;
2
3use bevy_ecs::{
4    change_detection::DetectChangesMut,
5    resource::Resource,
6    system::ResMut,
7    world::{FromWorld, World},
8};
9
10use super::{freely_mutable_state::FreelyMutableState, states::States};
11
12#[cfg(feature = "bevy_reflect")]
13use bevy_ecs::prelude::ReflectResource;
14
15#[cfg(feature = "bevy_reflect")]
16use bevy_reflect::prelude::ReflectDefault;
17
18/// A finite-state machine whose transitions have associated schedules
19/// ([`OnEnter(state)`](crate::state::OnEnter) and [`OnExit(state)`](crate::state::OnExit)).
20///
21/// The current state value can be accessed through this resource. To *change* the state,
22/// queue a transition in the [`NextState<S>`] resource, and it will be applied during the
23/// [`StateTransition`](crate::state::StateTransition) schedule - which by default runs after `PreUpdate`.
24///
25/// You can also manually trigger the [`StateTransition`](crate::state::StateTransition) schedule to apply the changes
26/// at an arbitrary time.
27///
28/// The starting state is defined via the [`Default`] implementation for `S`.
29///
30/// ```
31/// use bevy_state::prelude::*;
32/// use bevy_ecs::prelude::*;
33/// use bevy_state_macros::States;
34///
35/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
36/// enum GameState {
37///     #[default]
38///     MainMenu,
39///     SettingsMenu,
40///     InGame,
41/// }
42///
43/// fn game_logic(game_state: Res<State<GameState>>) {
44///     match game_state.get() {
45///         GameState::InGame => {
46///             // Run game logic here...
47///         },
48///         _ => {},
49///     }
50/// }
51/// ```
52#[derive(Resource, Debug)]
53#[cfg_attr(
54    feature = "bevy_reflect",
55    derive(bevy_reflect::Reflect),
56    reflect(Resource, Debug, PartialEq)
57)]
58pub struct State<S: States>(pub(crate) S);
59
60impl<S: States> State<S> {
61    /// Creates a new state with a specific value.
62    ///
63    /// To change the state use [`NextState<S>`] rather than using this to modify the `State<S>`.
64    pub fn new(state: S) -> Self {
65        Self(state)
66    }
67
68    /// Get the current state.
69    pub fn get(&self) -> &S {
70        &self.0
71    }
72}
73
74impl<S: States + FromWorld> FromWorld for State<S> {
75    fn from_world(world: &mut World) -> Self {
76        Self(S::from_world(world))
77    }
78}
79
80impl<S: States> PartialEq<S> for State<S> {
81    fn eq(&self, other: &S) -> bool {
82        self.get() == other
83    }
84}
85
86impl<S: States> Deref for State<S> {
87    type Target = S;
88
89    fn deref(&self) -> &Self::Target {
90        self.get()
91    }
92}
93
94/// The next state of [`State<S>`].
95///
96/// This can be fetched as a resource and used to queue state transitions.
97/// To queue a transition, call [`NextState::set`] or mutate the value to [`NextState::Pending`] directly.
98///
99/// Note that these transitions can be overridden by other systems:
100/// only the actual value of this resource during the [`StateTransition`](crate::state::StateTransition) schedule matters.
101///
102/// ```
103/// use bevy_state::prelude::*;
104/// use bevy_ecs::prelude::*;
105///
106/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
107/// enum GameState {
108///     #[default]
109///     MainMenu,
110///     SettingsMenu,
111///     InGame,
112/// }
113///
114/// fn start_game(mut next_game_state: ResMut<NextState<GameState>>) {
115///     next_game_state.set(GameState::InGame);
116/// }
117/// ```
118#[derive(Resource, Debug, Default, Clone)]
119#[cfg_attr(
120    feature = "bevy_reflect",
121    derive(bevy_reflect::Reflect),
122    reflect(Resource, Default, Debug)
123)]
124pub enum NextState<S: FreelyMutableState> {
125    /// No state transition is pending
126    #[default]
127    Unchanged,
128    /// There is a pending transition for state `S`
129    Pending(S),
130}
131
132impl<S: FreelyMutableState> NextState<S> {
133    /// Tentatively set a pending state transition to `Some(state)`.
134    pub fn set(&mut self, state: S) {
135        *self = Self::Pending(state);
136    }
137
138    /// Remove any pending changes to [`State<S>`]
139    pub fn reset(&mut self) {
140        *self = Self::Unchanged;
141    }
142}
143
144pub(crate) fn take_next_state<S: FreelyMutableState>(
145    next_state: Option<ResMut<NextState<S>>>,
146) -> Option<S> {
147    let mut next_state = next_state?;
148
149    match core::mem::take(next_state.bypass_change_detection()) {
150        NextState::Pending(x) => {
151            next_state.set_changed();
152            Some(x)
153        }
154        NextState::Unchanged => None,
155    }
156}