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
9374 views
1
use bevy_ecs::{
2
message::{MessageReader, MessageWriter},
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, PreviousState, State, StateTransitionEvent,
14
StateTransitionSystems, 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
/// [`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: MessageReader<StateTransitionEvent<S::RawState>>,
99
event: MessageWriter<StateTransitionEvent<T>>,
100
commands: Commands,
101
current_state: Option<ResMut<State<T>>>,
102
previous_state: Option<ResMut<PreviousState<T>>>,
103
state_set: Option<Res<State<S::RawState>>>| {
104
if parent_changed.is_empty() {
105
return;
106
}
107
parent_changed.clear();
108
109
let new_state =
110
if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {
111
T::compute(state_set)
112
} else {
113
None
114
};
115
116
internal_apply_state_transition(
117
event,
118
commands,
119
current_state,
120
previous_state,
121
new_state,
122
T::ALLOW_SAME_STATE_TRANSITIONS,
123
);
124
};
125
126
schedule.configure_sets((
127
ApplyStateTransition::<T>::default()
128
.in_set(StateTransitionSystems::DependentTransitions)
129
.after(ApplyStateTransition::<S::RawState>::default()),
130
ExitSchedules::<T>::default()
131
.in_set(StateTransitionSystems::ExitSchedules)
132
.before(ExitSchedules::<S::RawState>::default()),
133
TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),
134
EnterSchedules::<T>::default()
135
.in_set(StateTransitionSystems::EnterSchedules)
136
.after(EnterSchedules::<S::RawState>::default()),
137
));
138
139
schedule
140
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
141
.add_systems(
142
last_transition::<T>
143
.pipe(run_exit::<T>)
144
.in_set(ExitSchedules::<T>::default()),
145
)
146
.add_systems(
147
last_transition::<T>
148
.pipe(run_transition::<T>)
149
.in_set(TransitionSchedules::<T>::default()),
150
)
151
.add_systems(
152
last_transition::<T>
153
.pipe(run_enter::<T>)
154
.in_set(EnterSchedules::<T>::default()),
155
);
156
}
157
158
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
159
schedule: &mut Schedule,
160
) {
161
// | parent changed | next state | already exists | should exist | what happens |
162
// | -------------- | ---------- | -------------- | ------------ | -------------------------------- |
163
// | false | false | false | - | - |
164
// | false | false | true | - | - |
165
// | false | true | false | false | - |
166
// | true | false | false | false | - |
167
// | true | true | false | false | - |
168
// | true | false | true | false | Some(current) -> None |
169
// | true | true | true | false | Some(current) -> None |
170
// | true | false | false | true | None -> Some(default) |
171
// | true | true | false | true | None -> Some(next) |
172
// | true | true | true | true | Some(current) -> Some(next) |
173
// | false | true | true | true | Some(current) -> Some(next) |
174
// | true | false | true | true | Some(current) -> Some(current) |
175
176
let apply_state_transition =
177
|mut parent_changed: MessageReader<StateTransitionEvent<S::RawState>>,
178
event: MessageWriter<StateTransitionEvent<T>>,
179
commands: Commands,
180
current_state_res: Option<ResMut<State<T>>>,
181
previous_state: Option<ResMut<PreviousState<T>>>,
182
next_state_res: Option<ResMut<NextState<T>>>,
183
state_set: Option<Res<State<S::RawState>>>| {
184
let parent_changed = parent_changed.read().last().is_some();
185
let next_state = take_next_state(next_state_res);
186
187
if !parent_changed && next_state.is_none() {
188
return;
189
}
190
191
let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();
192
193
let initial_state = if parent_changed {
194
if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {
195
T::should_exist(state_set)
196
} else {
197
None
198
}
199
} else {
200
current_state.clone()
201
};
202
let same_state_enforced = next_state
203
.as_ref()
204
.map(|(_, same_state_enforced)| same_state_enforced)
205
.cloned()
206
.unwrap_or_default();
207
208
let new_state = initial_state.map(|x| {
209
next_state
210
.map(|(next, _)| next)
211
.or(current_state)
212
.unwrap_or(x)
213
});
214
215
internal_apply_state_transition(
216
event,
217
commands,
218
current_state_res,
219
previous_state,
220
new_state,
221
same_state_enforced,
222
);
223
};
224
225
schedule.configure_sets((
226
ApplyStateTransition::<T>::default()
227
.in_set(StateTransitionSystems::DependentTransitions)
228
.after(ApplyStateTransition::<S::RawState>::default()),
229
ExitSchedules::<T>::default()
230
.in_set(StateTransitionSystems::ExitSchedules)
231
.before(ExitSchedules::<S::RawState>::default()),
232
TransitionSchedules::<T>::default().in_set(StateTransitionSystems::TransitionSchedules),
233
EnterSchedules::<T>::default()
234
.in_set(StateTransitionSystems::EnterSchedules)
235
.after(EnterSchedules::<S::RawState>::default()),
236
));
237
238
schedule
239
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
240
.add_systems(
241
last_transition::<T>
242
.pipe(run_exit::<T>)
243
.in_set(ExitSchedules::<T>::default()),
244
)
245
.add_systems(
246
last_transition::<T>
247
.pipe(run_transition::<T>)
248
.in_set(TransitionSchedules::<T>::default()),
249
)
250
.add_systems(
251
last_transition::<T>
252
.pipe(run_enter::<T>)
253
.in_set(EnterSchedules::<T>::default()),
254
);
255
}
256
}
257
258
macro_rules! impl_state_set_sealed_tuples {
259
($(#[$meta:meta])* $(($param: ident, $val: ident, $evt: ident)), *) => {
260
$(#[$meta])*
261
impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}
262
263
$(#[$meta])*
264
impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {
265
266
const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;
267
268
269
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
270
schedule: &mut Schedule,
271
) {
272
let apply_state_transition =
273
|($(mut $evt),*,): ($(MessageReader<StateTransitionEvent<$param::RawState>>),*,),
274
message: MessageWriter<StateTransitionEvent<T>>,
275
commands: Commands,
276
current_state: Option<ResMut<State<T>>>,
277
previous_state: Option<ResMut<PreviousState<T>>>,
278
($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {
279
if ($($evt.is_empty())&&*) {
280
return;
281
}
282
$($evt.clear();)*
283
284
let new_state = if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {
285
T::compute(($($val),*, ))
286
} else {
287
None
288
};
289
290
internal_apply_state_transition(message, commands, current_state, previous_state, new_state, false);
291
};
292
293
schedule.configure_sets((
294
ApplyStateTransition::<T>::default()
295
.in_set(StateTransitionSystems::DependentTransitions)
296
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
297
ExitSchedules::<T>::default()
298
.in_set(StateTransitionSystems::ExitSchedules)
299
$(.before(ExitSchedules::<$param::RawState>::default()))*,
300
TransitionSchedules::<T>::default()
301
.in_set(StateTransitionSystems::TransitionSchedules),
302
EnterSchedules::<T>::default()
303
.in_set(StateTransitionSystems::EnterSchedules)
304
$(.after(EnterSchedules::<$param::RawState>::default()))*,
305
));
306
307
schedule
308
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
309
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
310
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
311
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
312
}
313
314
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
315
schedule: &mut Schedule,
316
) {
317
let apply_state_transition =
318
|($(mut $evt),*,): ($(MessageReader<StateTransitionEvent<$param::RawState>>),*,),
319
message: MessageWriter<StateTransitionEvent<T>>,
320
commands: Commands,
321
current_state_res: Option<ResMut<State<T>>>,
322
previous_state: Option<ResMut<PreviousState<T>>>,
323
next_state_res: Option<ResMut<NextState<T>>>,
324
($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {
325
let parent_changed = ($($evt.read().last().is_some())||*);
326
let next_state = take_next_state(next_state_res);
327
328
if !parent_changed && next_state.is_none() {
329
return;
330
}
331
332
let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();
333
334
let initial_state = if parent_changed {
335
if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {
336
T::should_exist(($($val),*, ))
337
} else {
338
None
339
}
340
} else {
341
current_state.clone()
342
};
343
344
let same_state_enforced = next_state
345
.as_ref()
346
.map(|(_, same_state_enforced)| same_state_enforced)
347
.cloned()
348
.unwrap_or_default();
349
350
let new_state = initial_state.map(|x| {
351
next_state
352
.map(|(next, _)| next)
353
.or(current_state)
354
.unwrap_or(x)
355
});
356
357
internal_apply_state_transition(message, commands, current_state_res, previous_state, new_state, same_state_enforced);
358
};
359
360
schedule.configure_sets((
361
ApplyStateTransition::<T>::default()
362
.in_set(StateTransitionSystems::DependentTransitions)
363
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
364
ExitSchedules::<T>::default()
365
.in_set(StateTransitionSystems::ExitSchedules)
366
$(.before(ExitSchedules::<$param::RawState>::default()))*,
367
TransitionSchedules::<T>::default()
368
.in_set(StateTransitionSystems::TransitionSchedules),
369
EnterSchedules::<T>::default()
370
.in_set(StateTransitionSystems::EnterSchedules)
371
$(.after(EnterSchedules::<$param::RawState>::default()))*,
372
));
373
374
schedule
375
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
376
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
377
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
378
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
379
}
380
}
381
};
382
}
383
384
all_tuples!(
385
#[doc(fake_variadic)]
386
impl_state_set_sealed_tuples,
387
1,
388
15,
389
S,
390
s,
391
ereader
392
);
393
394