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}