Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/condition.rs
6595 views
1
use crate::state::{State, States};
2
use bevy_ecs::{change_detection::DetectChanges, system::Res};
3
4
/// A [`SystemCondition`](bevy_ecs::prelude::SystemCondition)-satisfying system that returns `true`
5
/// if the state machine exists.
6
///
7
/// # Example
8
///
9
/// ```
10
/// # use bevy_ecs::prelude::*;
11
/// # use bevy_state::prelude::*;
12
/// # use bevy_app::{App, Update};
13
/// # use bevy_state::app::StatesPlugin;
14
/// # #[derive(Resource, Default)]
15
/// # struct Counter(u8);
16
/// # let mut app = App::new();
17
/// # app
18
/// # .init_resource::<Counter>()
19
/// # .add_plugins(StatesPlugin);
20
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
21
/// enum GameState {
22
/// #[default]
23
/// Playing,
24
/// Paused,
25
/// }
26
///
27
/// app.add_systems(Update,
28
/// // `state_exists` will only return true if the
29
/// // given state exists
30
/// my_system.run_if(state_exists::<GameState>),
31
/// );
32
///
33
/// fn my_system(mut counter: ResMut<Counter>) {
34
/// counter.0 += 1;
35
/// }
36
///
37
/// // `GameState` does not yet exist so `my_system` won't run
38
/// app.update();
39
/// assert_eq!(app.world().resource::<Counter>().0, 0);
40
///
41
/// app.init_state::<GameState>();
42
///
43
/// // `GameState` now exists so `my_system` will run
44
/// app.update();
45
/// assert_eq!(app.world().resource::<Counter>().0, 1);
46
/// ```
47
pub fn state_exists<S: States>(current_state: Option<Res<State<S>>>) -> bool {
48
current_state.is_some()
49
}
50
51
/// Generates a [`SystemCondition`](bevy_ecs::prelude::SystemCondition)-satisfying closure that returns `true`
52
/// if the state machine is currently in `state`.
53
///
54
/// Will return `false` if the state does not exist or if not in `state`.
55
///
56
/// # Example
57
///
58
/// ```
59
/// # use bevy_ecs::prelude::*;
60
/// # use bevy_state::prelude::*;
61
/// # use bevy_app::{App, Update};
62
/// # use bevy_state::app::StatesPlugin;
63
/// # #[derive(Resource, Default)]
64
/// # struct Counter(u8);
65
/// # let mut app = App::new();
66
/// # app
67
/// # .init_resource::<Counter>()
68
/// # .add_plugins(StatesPlugin);
69
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
70
/// enum GameState {
71
/// #[default]
72
/// Playing,
73
/// Paused,
74
/// }
75
///
76
/// app
77
/// .init_state::<GameState>()
78
/// .add_systems(Update, (
79
/// // `in_state` will only return true if the
80
/// // given state equals the given value
81
/// play_system.run_if(in_state(GameState::Playing)),
82
/// pause_system.run_if(in_state(GameState::Paused)),
83
/// ));
84
///
85
/// fn play_system(mut counter: ResMut<Counter>) {
86
/// counter.0 += 1;
87
/// }
88
///
89
/// fn pause_system(mut counter: ResMut<Counter>) {
90
/// counter.0 -= 1;
91
/// }
92
///
93
/// // We default to `GameState::Playing` so `play_system` runs
94
/// app.update();
95
/// assert_eq!(app.world().resource::<Counter>().0, 1);
96
///
97
/// app.insert_state(GameState::Paused);
98
///
99
/// // Now that we are in `GameState::Pause`, `pause_system` will run
100
/// app.update();
101
/// assert_eq!(app.world().resource::<Counter>().0, 0);
102
/// ```
103
pub fn in_state<S: States>(state: S) -> impl FnMut(Option<Res<State<S>>>) -> bool + Clone {
104
move |current_state: Option<Res<State<S>>>| match current_state {
105
Some(current_state) => *current_state == state,
106
None => false,
107
}
108
}
109
110
/// A [`SystemCondition`](bevy_ecs::prelude::SystemCondition)-satisfying system that returns `true`
111
/// if the state machine changed state.
112
///
113
/// To do things on transitions to/from specific states, use their respective OnEnter/OnExit
114
/// schedules. Use this run condition if you want to detect any change, regardless of the value.
115
///
116
/// Returns false if the state does not exist or the state has not changed.
117
///
118
/// # Example
119
///
120
/// ```
121
/// # use bevy_ecs::prelude::*;
122
/// # use bevy_state::prelude::*;
123
/// # use bevy_state::app::StatesPlugin;
124
/// # use bevy_app::{App, Update};
125
/// # #[derive(Resource, Default)]
126
/// # struct Counter(u8);
127
/// # let mut app = App::new();
128
/// # app
129
/// # .init_resource::<Counter>()
130
/// # .add_plugins(StatesPlugin);
131
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
132
/// enum GameState {
133
/// #[default]
134
/// Playing,
135
/// Paused,
136
/// }
137
///
138
/// app
139
/// .init_state::<GameState>()
140
/// .add_systems(Update,
141
/// // `state_changed` will only return true if the
142
/// // given states value has just been updated or
143
/// // the state has just been added
144
/// my_system.run_if(state_changed::<GameState>),
145
/// );
146
///
147
/// fn my_system(mut counter: ResMut<Counter>) {
148
/// counter.0 += 1;
149
/// }
150
///
151
/// // `GameState` has just been added so `my_system` will run
152
/// app.update();
153
/// assert_eq!(app.world().resource::<Counter>().0, 1);
154
///
155
/// // `GameState` has not been updated so `my_system` will not run
156
/// app.update();
157
/// assert_eq!(app.world().resource::<Counter>().0, 1);
158
///
159
/// app.insert_state(GameState::Paused);
160
///
161
/// // Now that `GameState` has been updated `my_system` will run
162
/// app.update();
163
/// assert_eq!(app.world().resource::<Counter>().0, 2);
164
/// ```
165
pub fn state_changed<S: States>(current_state: Option<Res<State<S>>>) -> bool {
166
let Some(current_state) = current_state else {
167
return false;
168
};
169
current_state.is_changed()
170
}
171
172
#[cfg(test)]
173
mod tests {
174
use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule, SystemCondition};
175
176
use crate::prelude::*;
177
use bevy_state_macros::States;
178
179
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
180
enum TestState {
181
#[default]
182
A,
183
B,
184
}
185
186
fn test_system() {}
187
188
// Ensure distributive_run_if compiles with the common conditions.
189
#[test]
190
fn distributive_run_if_compiles() {
191
Schedule::default().add_systems(
192
(test_system, test_system)
193
.distributive_run_if(state_exists::<TestState>)
194
.distributive_run_if(in_state(TestState::A).or(in_state(TestState::B)))
195
.distributive_run_if(state_changed::<TestState>),
196
);
197
}
198
}
199
200