Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/state/resources.rs
9366 views
1
use core::ops::Deref;
2
3
use bevy_ecs::{
4
change_detection::DetectChangesMut,
5
resource::Resource,
6
system::ResMut,
7
world::{FromWorld, World},
8
};
9
10
use super::{freely_mutable_state::FreelyMutableState, states::States};
11
12
#[cfg(feature = "bevy_reflect")]
13
use bevy_ecs::prelude::ReflectResource;
14
15
#[cfg(feature = "bevy_reflect")]
16
use 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
)]
58
pub struct State<S: States>(pub(crate) S);
59
60
impl<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
74
impl<S: States + FromWorld> FromWorld for State<S> {
75
fn from_world(world: &mut World) -> Self {
76
Self(S::from_world(world))
77
}
78
}
79
80
impl<S: States> PartialEq<S> for State<S> {
81
fn eq(&self, other: &S) -> bool {
82
self.get() == other
83
}
84
}
85
86
impl<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 previous state of [`State<S>`].
95
///
96
/// This resource holds the state value that was active immediately **before** the
97
/// most recent state transition. It is primarily useful for logic that runs
98
/// during state exit or transition schedules ([`OnExit`](crate::state::OnExit), [`OnTransition`](crate::state::OnTransition)).
99
///
100
/// It is inserted into the world only after the first state transition occurs. It will
101
/// remain present even if the primary state is removed (e.g., when a
102
/// [`SubStates`](crate::state::SubStates) or [`ComputedStates`](crate::state::ComputedStates) instance ceases to exist).
103
///
104
/// Use `Option<Res<PreviousState<S>>>` to access it, as it will not exist
105
/// before the first transition.
106
///
107
/// ```
108
/// use bevy_state::prelude::*;
109
/// use bevy_ecs::prelude::*;
110
/// use bevy_state_macros::States;
111
///
112
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
113
/// enum GameState {
114
/// #[default]
115
/// MainMenu,
116
/// InGame,
117
/// }
118
///
119
/// // This system might run in an OnExit schedule
120
/// fn log_previous_state(previous_state: Option<Res<PreviousState<GameState>>>) {
121
/// if let Some(previous) = previous_state {
122
/// // If this system is in OnExit(InGame), the previous state is what we
123
/// // were in before InGame.
124
/// println!("Transitioned from: {:?}", previous.get());
125
/// }
126
/// }
127
/// ```
128
#[derive(Resource, Debug, Clone, PartialEq, Eq)]
129
#[cfg_attr(
130
feature = "bevy_reflect",
131
derive(bevy_reflect::Reflect),
132
reflect(Resource, Debug, PartialEq)
133
)]
134
pub struct PreviousState<S: States>(pub(crate) S);
135
136
impl<S: States> PreviousState<S> {
137
/// Get the previous state.
138
pub fn get(&self) -> &S {
139
&self.0
140
}
141
}
142
143
impl<S: States> Deref for PreviousState<S> {
144
type Target = S;
145
146
fn deref(&self) -> &Self::Target {
147
&self.0
148
}
149
}
150
151
/// The next state of [`State<S>`].
152
///
153
/// This can be fetched as a resource and used to queue state transitions.
154
/// To queue a transition, call [`NextState::set`] or mutate the value to [`NextState::Pending`] directly.
155
///
156
/// Note that these transitions can be overridden by other systems:
157
/// only the actual value of this resource during the [`StateTransition`](crate::state::StateTransition) schedule matters.
158
///
159
/// ```
160
/// use bevy_state::prelude::*;
161
/// use bevy_ecs::prelude::*;
162
///
163
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
164
/// enum GameState {
165
/// #[default]
166
/// MainMenu,
167
/// SettingsMenu,
168
/// InGame,
169
/// }
170
///
171
/// fn start_game(mut next_game_state: ResMut<NextState<GameState>>) {
172
/// next_game_state.set(GameState::InGame);
173
/// }
174
/// ```
175
#[derive(Resource, Debug, Default, Clone)]
176
#[cfg_attr(
177
feature = "bevy_reflect",
178
derive(bevy_reflect::Reflect),
179
reflect(Resource, Default, Debug)
180
)]
181
pub enum NextState<S: FreelyMutableState> {
182
/// No state transition is pending
183
#[default]
184
Unchanged,
185
/// There is a pending transition for state `S`
186
Pending(S),
187
/// There is a pending transition for state `S`
188
///
189
/// This will not trigger state transitions schedules if the target state is the same as the current one.
190
PendingIfNeq(S),
191
}
192
193
impl<S: FreelyMutableState> NextState<S> {
194
/// Tentatively set a pending state transition to `Some(state)`.
195
///
196
/// This will run the state transition schedules [`OnEnter`](crate::state::OnEnter) and [`OnExit`](crate::state::OnExit).
197
/// 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.
198
pub fn set(&mut self, state: S) {
199
*self = Self::Pending(state);
200
}
201
202
/// Tentatively set a pending state transition to `Some(state)`.
203
///
204
/// Like [`set`](Self::set), but will not run any state transition schedules if the target state is the same as the current one.
205
/// If [`set`](Self::set) has already been called in the same frame with the same state, the transition schedules will be run anyways.
206
pub fn set_if_neq(&mut self, state: S) {
207
if !matches!(self, Self::Pending(s) if s == &state) {
208
*self = Self::PendingIfNeq(state);
209
}
210
}
211
212
/// Remove any pending changes to [`State<S>`]
213
pub fn reset(&mut self) {
214
*self = Self::Unchanged;
215
}
216
}
217
218
pub(crate) fn take_next_state<S: FreelyMutableState>(
219
next_state: Option<ResMut<NextState<S>>>,
220
) -> Option<(S, bool)> {
221
let mut next_state = next_state?;
222
223
match core::mem::take(next_state.bypass_change_detection()) {
224
NextState::Pending(x) => {
225
next_state.set_changed();
226
Some((x, true))
227
}
228
NextState::PendingIfNeq(x) => {
229
next_state.set_changed();
230
Some((x, false))
231
}
232
NextState::Unchanged => None,
233
}
234
}
235
236