Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/state/state_set.rs
6596 views
1
use bevy_ecs::{
2
event::{EventReader, EventWriter},
3
schedule::{IntoScheduleConfigs, Schedule},
4
system::{Commands, IntoSystem, Res, ResMut},
5
};
6
use variadics_please::all_tuples;
7
8
use self::sealed::StateSetSealed;
9
10
use super::{
11
computed_states::ComputedStates, internal_apply_state_transition, last_transition, run_enter,
12
run_exit, run_transition, sub_states::SubStates, take_next_state, ApplyStateTransition,
13
EnterSchedules, ExitSchedules, NextState, State, StateTransitionEvent, StateTransitionSystems,
14
States, TransitionSchedules,
15
};
16
17
mod sealed {
18
/// Sealed trait used to prevent external implementations of [`StateSet`](super::StateSet).
19
pub trait StateSetSealed {}
20
}
21
22
/// A [`States`] type or tuple of types which implement [`States`].
23
///
24
/// This trait is used to allow implementors of [`States`], as well
25
/// as tuples containing exclusively implementors of [`States`], to
26
/// be used as [`ComputedStates::SourceStates`].
27
///
28
/// It is sealed, and auto implemented for all [`States`] types and
29
/// tuples containing them.
30
pub trait StateSet: StateSetSealed {
31
/// The total [`DEPENDENCY_DEPTH`](`States::DEPENDENCY_DEPTH`) of all
32
/// the states that are part of this [`StateSet`], added together.
33
///
34
/// Used to de-duplicate computed state executions and prevent cyclic
35
/// computed states.
36
const SET_DEPENDENCY_DEPTH: usize;
37
38
/// Sets up the systems needed to compute `T` whenever any `State` in this
39
/// `StateSet` is changed.
40
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
41
schedule: &mut Schedule,
42
);
43
44
/// Sets up the systems needed to compute whether `T` exists whenever any `State` in this
45
/// `StateSet` is changed.
46
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
47
schedule: &mut Schedule,
48
);
49
}
50
51
/// The `InnerStateSet` trait is used to isolate [`ComputedStates`] & [`SubStates`] from
52
/// needing to wrap all state dependencies in an [`Option<S>`].
53
///
54
/// Some [`ComputedStates`]'s might need to exist in different states based on the existence
55
/// of other states. So we needed the ability to use[`Option<S>`] when appropriate.
56
///
57
/// The isolation works because it is implemented for both S & [`Option<S>`], and has the `RawState` associated type
58
/// that allows it to know what the resource in the world should be. We can then essentially "unwrap" it in our
59
/// `StateSet` implementation - and the behavior of that unwrapping will depend on the arguments expected by the
60
/// the [`ComputedStates`] & [`SubStates]`.
61
trait InnerStateSet: Sized {
62
type RawState: States;
63
64
const DEPENDENCY_DEPTH: usize;
65
66
fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self>;
67
}
68
69
impl<S: States> InnerStateSet for S {
70
type RawState = Self;
71
72
const DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;
73
74
fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self> {
75
wrapped.map(|v| v.0.clone())
76
}
77
}
78
79
impl<S: States> InnerStateSet for Option<S> {
80
type RawState = S;
81
82
const DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;
83
84
fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self> {
85
Some(wrapped.map(|v| v.0.clone()))
86
}
87
}
88
89
impl<S: InnerStateSet> StateSetSealed for S {}
90
91
impl<S: InnerStateSet> StateSet for S {
92
const SET_DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;
93
94
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
95
schedule: &mut Schedule,
96
) {
97
let apply_state_transition =
98
|mut parent_changed: EventReader<StateTransitionEvent<S::RawState>>,
99
event: EventWriter<StateTransitionEvent<T>>,
100
commands: Commands,
101
current_state: Option<ResMut<State<T>>>,
102
state_set: Option<Res<State<S::RawState>>>| {
103
if parent_changed.is_empty() {
104
return;
105
}
106
parent_changed.clear();
107
108
let new_state =
109
if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {
110
T::compute(state_set)
111
} else {
112
None
113
};
114
115
internal_apply_state_transition(event, commands, current_state, new_state);
116
};
117
118
schedule.configure_sets((
119
ApplyStateTransition::<T>::default()
120
.in_set(StateTransitionSystems::DependentTransitions)
121
.after(ApplyStateTransition::<S::RawState>::default()),
122
ExitSchedules::<T>::default()
123
.in_set(StateTransitionSystems::ExitSchedules)
124
.before(ExitSchedules::<S::RawState>::default()),
125
TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),
126
EnterSchedules::<T>::default()
127
.in_set(StateTransitionSystems::EnterSchedules)
128
.after(EnterSchedules::<S::RawState>::default()),
129
));
130
131
schedule
132
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
133
.add_systems(
134
last_transition::<T>
135
.pipe(run_exit::<T>)
136
.in_set(ExitSchedules::<T>::default()),
137
)
138
.add_systems(
139
last_transition::<T>
140
.pipe(run_transition::<T>)
141
.in_set(TransitionSchedules::<T>::default()),
142
)
143
.add_systems(
144
last_transition::<T>
145
.pipe(run_enter::<T>)
146
.in_set(EnterSchedules::<T>::default()),
147
);
148
}
149
150
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
151
schedule: &mut Schedule,
152
) {
153
// | parent changed | next state | already exists | should exist | what happens |
154
// | -------------- | ---------- | -------------- | ------------ | -------------------------------- |
155
// | false | false | false | - | - |
156
// | false | false | true | - | - |
157
// | false | true | false | false | - |
158
// | true | false | false | false | - |
159
// | true | true | false | false | - |
160
// | true | false | true | false | Some(current) -> None |
161
// | true | true | true | false | Some(current) -> None |
162
// | true | false | false | true | None -> Some(default) |
163
// | true | true | false | true | None -> Some(next) |
164
// | true | true | true | true | Some(current) -> Some(next) |
165
// | false | true | true | true | Some(current) -> Some(next) |
166
// | true | false | true | true | Some(current) -> Some(current) |
167
168
let apply_state_transition =
169
|mut parent_changed: EventReader<StateTransitionEvent<S::RawState>>,
170
event: EventWriter<StateTransitionEvent<T>>,
171
commands: Commands,
172
current_state_res: Option<ResMut<State<T>>>,
173
next_state_res: Option<ResMut<NextState<T>>>,
174
state_set: Option<Res<State<S::RawState>>>| {
175
let parent_changed = parent_changed.read().last().is_some();
176
let next_state = take_next_state(next_state_res);
177
178
if !parent_changed && next_state.is_none() {
179
return;
180
}
181
182
let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();
183
184
let initial_state = if parent_changed {
185
if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {
186
T::should_exist(state_set)
187
} else {
188
None
189
}
190
} else {
191
current_state.clone()
192
};
193
let new_state = initial_state.map(|x| next_state.or(current_state).unwrap_or(x));
194
195
internal_apply_state_transition(event, commands, current_state_res, new_state);
196
};
197
198
schedule.configure_sets((
199
ApplyStateTransition::<T>::default()
200
.in_set(StateTransitionSystems::DependentTransitions)
201
.after(ApplyStateTransition::<S::RawState>::default()),
202
ExitSchedules::<T>::default()
203
.in_set(StateTransitionSystems::ExitSchedules)
204
.before(ExitSchedules::<S::RawState>::default()),
205
TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),
206
EnterSchedules::<T>::default()
207
.in_set(StateTransitionSystems::EnterSchedules)
208
.after(EnterSchedules::<S::RawState>::default()),
209
));
210
211
schedule
212
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
213
.add_systems(
214
last_transition::<T>
215
.pipe(run_exit::<T>)
216
.in_set(ExitSchedules::<T>::default()),
217
)
218
.add_systems(
219
last_transition::<T>
220
.pipe(run_transition::<T>)
221
.in_set(TransitionSchedules::<T>::default()),
222
)
223
.add_systems(
224
last_transition::<T>
225
.pipe(run_enter::<T>)
226
.in_set(EnterSchedules::<T>::default()),
227
);
228
}
229
}
230
231
macro_rules! impl_state_set_sealed_tuples {
232
($(#[$meta:meta])* $(($param: ident, $val: ident, $evt: ident)), *) => {
233
$(#[$meta])*
234
impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}
235
236
$(#[$meta])*
237
impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {
238
239
const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;
240
241
242
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
243
schedule: &mut Schedule,
244
) {
245
let apply_state_transition =
246
|($(mut $evt),*,): ($(EventReader<StateTransitionEvent<$param::RawState>>),*,),
247
event: EventWriter<StateTransitionEvent<T>>,
248
commands: Commands,
249
current_state: Option<ResMut<State<T>>>,
250
($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {
251
if ($($evt.is_empty())&&*) {
252
return;
253
}
254
$($evt.clear();)*
255
256
let new_state = if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {
257
T::compute(($($val),*, ))
258
} else {
259
None
260
};
261
262
internal_apply_state_transition(event, commands, current_state, new_state);
263
};
264
265
schedule.configure_sets((
266
ApplyStateTransition::<T>::default()
267
.in_set(StateTransitionSystems::DependentTransitions)
268
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
269
ExitSchedules::<T>::default()
270
.in_set(StateTransitionSystems::ExitSchedules)
271
$(.before(ExitSchedules::<$param::RawState>::default()))*,
272
TransitionSchedules::<T>::default()
273
.in_set(StateTransitionSystems::TransitionSchedules),
274
EnterSchedules::<T>::default()
275
.in_set(StateTransitionSystems::EnterSchedules)
276
$(.after(EnterSchedules::<$param::RawState>::default()))*,
277
));
278
279
schedule
280
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
281
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
282
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
283
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
284
}
285
286
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
287
schedule: &mut Schedule,
288
) {
289
let apply_state_transition =
290
|($(mut $evt),*,): ($(EventReader<StateTransitionEvent<$param::RawState>>),*,),
291
event: EventWriter<StateTransitionEvent<T>>,
292
commands: Commands,
293
current_state_res: Option<ResMut<State<T>>>,
294
next_state_res: Option<ResMut<NextState<T>>>,
295
($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {
296
let parent_changed = ($($evt.read().last().is_some())||*);
297
let next_state = take_next_state(next_state_res);
298
299
if !parent_changed && next_state.is_none() {
300
return;
301
}
302
303
let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();
304
305
let initial_state = if parent_changed {
306
if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {
307
T::should_exist(($($val),*, ))
308
} else {
309
None
310
}
311
} else {
312
current_state.clone()
313
};
314
let new_state = initial_state.map(|x| next_state.or(current_state).unwrap_or(x));
315
316
internal_apply_state_transition(event, commands, current_state_res, new_state);
317
};
318
319
schedule.configure_sets((
320
ApplyStateTransition::<T>::default()
321
.in_set(StateTransitionSystems::DependentTransitions)
322
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
323
ExitSchedules::<T>::default()
324
.in_set(StateTransitionSystems::ExitSchedules)
325
$(.before(ExitSchedules::<$param::RawState>::default()))*,
326
TransitionSchedules::<T>::default()
327
.in_set(StateTransitionSystems::TransitionSchedules),
328
EnterSchedules::<T>::default()
329
.in_set(StateTransitionSystems::EnterSchedules)
330
$(.after(EnterSchedules::<$param::RawState>::default()))*,
331
));
332
333
schedule
334
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
335
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
336
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
337
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
338
}
339
}
340
};
341
}
342
343
all_tuples!(
344
#[doc(fake_variadic)]
345
impl_state_set_sealed_tuples,
346
1,
347
15,
348
S,
349
s,
350
ereader
351
);
352
353