Skip to main content

bevy_state/state/
sub_states.rs

1use bevy_ecs::schedule::Schedule;
2
3use super::{freely_mutable_state::FreelyMutableState, state_set::StateSet, states::States};
4pub 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)]
148pub 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}