Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/state/computed_states.rs
6596 views
1
use core::{fmt::Debug, hash::Hash};
2
3
use bevy_ecs::schedule::Schedule;
4
5
use super::{state_set::StateSet, states::States};
6
7
/// A state whose value is automatically computed based on the values of other [`States`].
8
///
9
/// A **computed state** is a state that is deterministically derived from a set of `SourceStates`.
10
/// The [`StateSet`] is passed into the `compute` method whenever one of them changes, and the
11
/// result becomes the state's value.
12
///
13
/// ```
14
/// # use bevy_state::prelude::*;
15
/// # use bevy_ecs::prelude::*;
16
/// #
17
/// /// Computed States require some state to derive from
18
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
19
/// enum AppState {
20
/// #[default]
21
/// Menu,
22
/// InGame { paused: bool }
23
/// }
24
///
25
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
26
/// struct InGame;
27
///
28
/// impl ComputedStates for InGame {
29
/// /// We set the source state to be the state, or a tuple of states,
30
/// /// we want to depend on. You can also wrap each state in an Option,
31
/// /// if you want the computed state to execute even if the state doesn't
32
/// /// currently exist in the world.
33
/// type SourceStates = AppState;
34
///
35
/// /// We then define the compute function, which takes in
36
/// /// your SourceStates
37
/// fn compute(sources: AppState) -> Option<Self> {
38
/// match sources {
39
/// /// When we are in game, we want to return the InGame state
40
/// AppState::InGame { .. } => Some(InGame),
41
/// /// Otherwise, we don't want the `State<InGame>` resource to exist,
42
/// /// so we return None.
43
/// _ => None
44
/// }
45
/// }
46
/// }
47
/// ```
48
///
49
/// you can then add it to an App, and from there you use the state as normal
50
///
51
/// ```
52
/// # use bevy_state::prelude::*;
53
/// # use bevy_ecs::prelude::*;
54
/// #
55
/// # struct App;
56
/// # impl App {
57
/// # fn new() -> Self { App }
58
/// # fn init_state<S>(&mut self) -> &mut Self {self}
59
/// # fn add_computed_state<S>(&mut self) -> &mut Self {self}
60
/// # }
61
/// # struct AppState;
62
/// # struct InGame;
63
/// #
64
/// App::new()
65
/// .init_state::<AppState>()
66
/// .add_computed_state::<InGame>();
67
/// ```
68
pub trait ComputedStates: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug {
69
/// The set of states from which the [`Self`] is derived.
70
///
71
/// This can either be a single type that implements [`States`], an Option of a type
72
/// that implements [`States`], or a tuple
73
/// containing multiple types that implement [`States`] or Optional versions of them.
74
///
75
/// For example, `(MapState, EnemyState)` is valid, as is `(MapState, Option<EnemyState>)`
76
type SourceStates: StateSet;
77
78
/// Computes the next value of [`State<Self>`](crate::state::State).
79
/// This function gets called whenever one of the [`SourceStates`](Self::SourceStates) changes.
80
///
81
/// If the result is [`None`], the [`State<Self>`](crate::state::State) resource will be removed from the world.
82
fn compute(sources: Self::SourceStates) -> Option<Self>;
83
84
/// This function sets up systems that compute the state whenever one of the [`SourceStates`](Self::SourceStates)
85
/// change. It is called by `App::add_computed_state`, but can be called manually if `App` is not
86
/// used.
87
fn register_computed_state_systems(schedule: &mut Schedule) {
88
Self::SourceStates::register_computed_state_systems_in_schedule::<Self>(schedule);
89
}
90
}
91
92
impl<S: ComputedStates> States for S {
93
const DEPENDENCY_DEPTH: usize = S::SourceStates::SET_DEPENDENCY_DEPTH + 1;
94
}
95
96
#[cfg(test)]
97
mod tests {
98
use crate::{
99
app::{AppExtStates, StatesPlugin},
100
prelude::DespawnOnEnter,
101
state::{ComputedStates, StateTransition},
102
};
103
use bevy_app::App;
104
use bevy_ecs::component::Component;
105
use bevy_state_macros::States;
106
107
#[derive(Component)]
108
struct TestComponent;
109
110
#[derive(States, Default, PartialEq, Eq, Hash, Debug, Clone)]
111
struct TestState;
112
113
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
114
struct TestComputedState;
115
116
impl ComputedStates for TestComputedState {
117
type SourceStates = TestState;
118
119
fn compute(_: Self::SourceStates) -> Option<Self> {
120
Some(TestComputedState)
121
}
122
}
123
124
#[test]
125
fn computed_states_are_state_scoped_by_default() {
126
let mut app = App::new();
127
app.add_plugins(StatesPlugin);
128
app.insert_state(TestState);
129
app.add_computed_state::<TestComputedState>();
130
131
let world = app.world_mut();
132
133
world.spawn((DespawnOnEnter(TestComputedState), TestComponent));
134
135
assert!(world.query::<&TestComponent>().single(world).is_ok());
136
world.run_schedule(StateTransition);
137
assert_eq!(world.query::<&TestComponent>().iter(world).len(), 0);
138
}
139
}
140
141