Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_state/src/reflect.rs
9367 views
1
use crate::state::{FreelyMutableState, NextState, State, States};
2
3
use bevy_ecs::{reflect::from_reflect_with_fallback, world::World};
4
use bevy_reflect::{FromType, Reflect, TypePath, TypeRegistry};
5
6
/// A struct used to operate on the reflected [`States`] trait of a type.
7
///
8
/// A [`ReflectState`] for type `T` can be obtained via
9
/// [`bevy_reflect::TypeRegistration::data`].
10
#[derive(Clone)]
11
pub struct ReflectState(ReflectStateFns);
12
13
/// The raw function pointers needed to make up a [`ReflectState`].
14
#[derive(Clone)]
15
pub struct ReflectStateFns {
16
/// Function pointer implementing [`ReflectState::reflect()`].
17
pub reflect: fn(&World) -> Option<&dyn Reflect>,
18
}
19
20
impl ReflectStateFns {
21
/// Get the default set of [`ReflectStateFns`] for a specific component type using its
22
/// [`FromType`] implementation.
23
///
24
/// This is useful if you want to start with the default implementation before overriding some
25
/// of the functions to create a custom implementation.
26
pub fn new<T: States + Reflect>() -> Self {
27
<ReflectState as FromType<T>>::from_type().0
28
}
29
}
30
31
impl ReflectState {
32
/// Gets the value of this [`States`] type from the world as a reflected reference.
33
pub fn reflect<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> {
34
(self.0.reflect)(world)
35
}
36
}
37
38
impl<S: States + Reflect> FromType<S> for ReflectState {
39
fn from_type() -> Self {
40
ReflectState(ReflectStateFns {
41
reflect: |world| {
42
world
43
.get_resource::<State<S>>()
44
.map(|res| res.get() as &dyn Reflect)
45
},
46
})
47
}
48
}
49
50
/// A struct used to operate on the reflected [`FreelyMutableState`] trait of a type.
51
///
52
/// A [`ReflectFreelyMutableState`] for type `T` can be obtained via
53
/// [`bevy_reflect::TypeRegistration::data`].
54
#[derive(Clone)]
55
pub struct ReflectFreelyMutableState(ReflectFreelyMutableStateFns);
56
57
/// The raw function pointers needed to make up a [`ReflectFreelyMutableState`].
58
#[derive(Clone)]
59
pub struct ReflectFreelyMutableStateFns {
60
/// Function pointer implementing [`ReflectFreelyMutableState::set_next_state()`].
61
pub set_next_state: fn(&mut World, &dyn Reflect, &TypeRegistry),
62
/// Function pointer implementing [`ReflectFreelyMutableState::set_next_state_if_neq()`].
63
pub set_next_state_if_neq: fn(&mut World, &dyn Reflect, &TypeRegistry),
64
}
65
66
impl ReflectFreelyMutableStateFns {
67
/// Get the default set of [`ReflectFreelyMutableStateFns`] for a specific component type using its
68
/// [`FromType`] implementation.
69
///
70
/// This is useful if you want to start with the default implementation before overriding some
71
/// of the functions to create a custom implementation.
72
pub fn new<T: FreelyMutableState + Reflect + TypePath>() -> Self {
73
<ReflectFreelyMutableState as FromType<T>>::from_type().0
74
}
75
}
76
77
impl ReflectFreelyMutableState {
78
/// Tentatively set a pending state transition to a reflected [`ReflectFreelyMutableState`].
79
pub fn set_next_state(&self, world: &mut World, state: &dyn Reflect, registry: &TypeRegistry) {
80
(self.0.set_next_state)(world, state, registry);
81
}
82
/// Tentatively set a pending state transition to a reflected [`ReflectFreelyMutableState`], skipping state transitions if the target state is the same as the current state.
83
pub fn set_next_state_if_neq(
84
&self,
85
world: &mut World,
86
state: &dyn Reflect,
87
registry: &TypeRegistry,
88
) {
89
(self.0.set_next_state_if_neq)(world, state, registry);
90
}
91
}
92
93
impl<S: FreelyMutableState + Reflect + TypePath> FromType<S> for ReflectFreelyMutableState {
94
fn from_type() -> Self {
95
ReflectFreelyMutableState(ReflectFreelyMutableStateFns {
96
set_next_state: |world, reflected_state, registry| {
97
let new_state: S = from_reflect_with_fallback(
98
reflected_state.as_partial_reflect(),
99
world,
100
registry,
101
);
102
if let Some(mut next_state) = world.get_resource_mut::<NextState<S>>() {
103
next_state.set(new_state);
104
}
105
},
106
set_next_state_if_neq: |world, reflected_state, registry| {
107
let new_state: S = from_reflect_with_fallback(
108
reflected_state.as_partial_reflect(),
109
world,
110
registry,
111
);
112
if let Some(mut next_state) = world.get_resource_mut::<NextState<S>>() {
113
next_state.set_if_neq(new_state);
114
}
115
},
116
})
117
}
118
}
119
120
#[cfg(test)]
121
mod tests {
122
use crate::{
123
app::{AppExtStates, StatesPlugin},
124
reflect::{ReflectFreelyMutableState, ReflectState},
125
state::State,
126
};
127
use bevy_app::App;
128
use bevy_ecs::prelude::AppTypeRegistry;
129
use bevy_reflect::Reflect;
130
use bevy_state_macros::States;
131
use core::any::TypeId;
132
133
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, States, Reflect)]
134
enum StateTest {
135
A,
136
B,
137
}
138
139
#[test]
140
fn test_reflect_state_operations() {
141
let mut app = App::new();
142
app.add_plugins(StatesPlugin)
143
.insert_state(StateTest::A)
144
.register_type_mutable_state::<StateTest>();
145
146
let type_registry = app.world_mut().resource::<AppTypeRegistry>().0.clone();
147
let type_registry = type_registry.read();
148
149
let (reflect_state, reflect_mutable_state) = (
150
type_registry
151
.get_type_data::<ReflectState>(TypeId::of::<StateTest>())
152
.unwrap()
153
.clone(),
154
type_registry
155
.get_type_data::<ReflectFreelyMutableState>(TypeId::of::<StateTest>())
156
.unwrap()
157
.clone(),
158
);
159
160
let current_value = reflect_state.reflect(app.world()).unwrap();
161
assert_eq!(
162
current_value.downcast_ref::<StateTest>().unwrap(),
163
&StateTest::A
164
);
165
166
reflect_mutable_state.set_next_state(app.world_mut(), &StateTest::B, &type_registry);
167
168
assert_ne!(
169
app.world().resource::<State<StateTest>>().get(),
170
&StateTest::B
171
);
172
173
app.update();
174
175
assert_eq!(
176
app.world().resource::<State<StateTest>>().get(),
177
&StateTest::B
178
);
179
180
let current_value = reflect_state.reflect(app.world()).unwrap();
181
assert_eq!(
182
current_value.downcast_ref::<StateTest>().unwrap(),
183
&StateTest::B
184
);
185
}
186
}
187
188