Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/state/resources.rs
6596 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 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
)]
124
pub 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
132
impl<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
144
pub(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
}
157
158