Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/state/sub_states.rs
6596 views
1
use bevy_ecs::schedule::Schedule;
2
3
use super::{freely_mutable_state::FreelyMutableState, state_set::StateSet, states::States};
4
pub use bevy_state_macros::SubStates;
5
6
/// A sub-state is a state that exists only when the source state meet certain conditions,
7
/// but unlike [`ComputedStates`](crate::state::ComputedStates) - while they exist they can be manually modified.
8
///
9
/// The default approach to creating [`SubStates`] is using the derive macro, and defining a single source state
10
/// and value to determine its existence.
11
///
12
/// ```
13
/// # use bevy_ecs::prelude::*;
14
/// # use bevy_state::prelude::*;
15
///
16
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
17
/// enum AppState {
18
/// #[default]
19
/// Menu,
20
/// InGame
21
/// }
22
///
23
///
24
/// #[derive(SubStates, Clone, PartialEq, Eq, Hash, Debug, Default)]
25
/// #[source(AppState = AppState::InGame)]
26
/// enum GamePhase {
27
/// #[default]
28
/// Setup,
29
/// Battle,
30
/// Conclusion
31
/// }
32
/// ```
33
///
34
/// you can then add it to an App, and from there you use the state as normal:
35
///
36
/// ```
37
/// # use bevy_ecs::prelude::*;
38
/// # use bevy_state::prelude::*;
39
///
40
/// # struct App;
41
/// # impl App {
42
/// # fn new() -> Self { App }
43
/// # fn init_state<S>(&mut self) -> &mut Self {self}
44
/// # fn add_sub_state<S>(&mut self) -> &mut Self {self}
45
/// # }
46
/// # struct AppState;
47
/// # struct GamePhase;
48
///
49
/// App::new()
50
/// .init_state::<AppState>()
51
/// .add_sub_state::<GamePhase>();
52
/// ```
53
///
54
/// In more complex situations, the recommendation is to use an intermediary computed state, like so:
55
///
56
/// ```
57
/// # use bevy_ecs::prelude::*;
58
/// # use bevy_state::prelude::*;
59
///
60
/// /// Computed States require some state to derive from
61
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
62
/// enum AppState {
63
/// #[default]
64
/// Menu,
65
/// InGame { paused: bool }
66
/// }
67
///
68
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
69
/// struct InGame;
70
///
71
/// impl ComputedStates for InGame {
72
/// /// We set the source state to be the state, or set of states,
73
/// /// we want to depend on. Any of the states can be wrapped in an Option.
74
/// type SourceStates = Option<AppState>;
75
///
76
/// /// We then define the compute function, which takes in the AppState
77
/// fn compute(sources: Option<AppState>) -> Option<Self> {
78
/// match sources {
79
/// /// When we are in game, we want to return the InGame state
80
/// Some(AppState::InGame { .. }) => Some(InGame),
81
/// /// Otherwise, we don't want the `State<InGame>` resource to exist,
82
/// /// so we return None.
83
/// _ => None
84
/// }
85
/// }
86
/// }
87
///
88
/// #[derive(SubStates, Clone, PartialEq, Eq, Hash, Debug, Default)]
89
/// #[source(InGame = InGame)]
90
/// enum GamePhase {
91
/// #[default]
92
/// Setup,
93
/// Battle,
94
/// Conclusion
95
/// }
96
/// ```
97
///
98
/// However, you can also manually implement them. If you do so, you'll also need to manually implement the `States` & `FreelyMutableState` traits.
99
///
100
/// ```
101
/// # use bevy_ecs::prelude::*;
102
/// # use bevy_state::prelude::*;
103
/// # use bevy_state::state::{FreelyMutableState, NextState};
104
///
105
/// /// Computed States require some state to derive from
106
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
107
/// enum AppState {
108
/// #[default]
109
/// Menu,
110
/// InGame { paused: bool }
111
/// }
112
///
113
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
114
/// enum GamePhase {
115
/// Setup,
116
/// Battle,
117
/// Conclusion
118
/// }
119
///
120
/// impl SubStates for GamePhase {
121
/// /// We set the source state to be the state, or set of states,
122
/// /// we want to depend on. Any of the states can be wrapped in an Option.
123
/// type SourceStates = Option<AppState>;
124
///
125
/// /// We then define the compute function, which takes in the [`Self::SourceStates`]
126
/// fn should_exist(sources: Option<AppState>) -> Option<Self> {
127
/// match sources {
128
/// /// When we are in game, we want a GamePhase state to exist.
129
/// /// We can set the initial value here or overwrite it through [`NextState`].
130
/// Some(AppState::InGame { .. }) => Some(Self::Setup),
131
/// /// If we don't want the `State<GamePhase>` resource to exist we return [`None`].
132
/// _ => None
133
/// }
134
/// }
135
/// }
136
///
137
/// impl States for GamePhase {
138
/// const DEPENDENCY_DEPTH : usize = <GamePhase as SubStates>::SourceStates::SET_DEPENDENCY_DEPTH + 1;
139
/// }
140
///
141
/// impl FreelyMutableState for GamePhase {}
142
/// ```
143
#[diagnostic::on_unimplemented(
144
message = "`{Self}` can not be used as a sub-state",
145
label = "invalid sub-state",
146
note = "consider annotating `{Self}` with `#[derive(SubStates)]`"
147
)]
148
pub trait SubStates: States + FreelyMutableState {
149
/// The set of states from which the [`Self`] is derived.
150
///
151
/// This can either be a single type that implements [`States`], or a tuple
152
/// containing multiple types that implement [`States`], or any combination of
153
/// types implementing [`States`] and Options of types implementing [`States`].
154
type SourceStates: StateSet;
155
156
/// This function gets called whenever one of the [`SourceStates`](Self::SourceStates) changes.
157
/// The result is used to determine the existence of [`State<Self>`](crate::state::State).
158
///
159
/// If the result is [`None`], the [`State<Self>`](crate::state::State) resource will be removed from the world,
160
/// otherwise if the [`State<Self>`](crate::state::State) resource doesn't exist
161
/// it will be created from the returned [`Some`] as the initial state.
162
///
163
/// Value within [`Some`] is ignored if the state already exists in the world
164
/// and only symbolizes that the state should still exist.
165
///
166
/// Initial value can also be overwritten by [`NextState`](crate::state::NextState).
167
fn should_exist(sources: Self::SourceStates) -> Option<Self>;
168
169
/// This function sets up systems that compute the state whenever one of the [`SourceStates`](Self::SourceStates)
170
/// change. It is called by `App::add_computed_state`, but can be called manually if `App` is not
171
/// used.
172
fn register_sub_state_systems(schedule: &mut Schedule) {
173
Self::SourceStates::register_sub_state_systems_in_schedule::<Self>(schedule);
174
}
175
}
176
177