leafwing_input_manager/user_input/
updating.rs1use std::any::TypeId;
4use std::hash::Hash;
5use std::marker::PhantomData;
6
7use bevy::{
8 app::{App, PreUpdate},
9 ecs::{
10 schedule::IntoScheduleConfigs,
11 system::{StaticSystemParam, SystemParam},
12 },
13 math::{Vec2, Vec3},
14 platform::collections::{HashMap, HashSet},
15 prelude::{Res, ResMut, Resource},
16 reflect::Reflect,
17};
18
19use super::{Axislike, Buttonlike, DualAxislike, TripleAxislike};
20use crate::buttonlike::ButtonValue;
21use crate::{plugin::InputManagerSystem, InputControlKind};
22
23#[derive(Resource, Default, Debug, Reflect)]
30pub struct CentralInputStore {
31 updated_values: HashMap<TypeId, UpdatedValues>,
33 registered_input_kinds: HashSet<TypeId>,
35}
36
37impl CentralInputStore {
38 pub fn clear(&mut self) {
42 for map in self.updated_values.values_mut() {
45 match map {
46 UpdatedValues::Buttonlike(buttonlikes) => buttonlikes.clear(),
47 UpdatedValues::Axislike(axislikes) => axislikes.clear(),
48 UpdatedValues::Dualaxislike(dualaxislikes) => dualaxislikes.clear(),
49 UpdatedValues::Tripleaxislike(tripleaxislikes) => tripleaxislikes.clear(),
50 }
51 }
52 }
53
54 pub fn update_buttonlike<B: Buttonlike>(&mut self, buttonlike: B, value: ButtonValue) {
56 let updated_values = self
57 .updated_values
58 .entry(TypeId::of::<B>())
59 .or_insert_with(|| UpdatedValues::Buttonlike(HashMap::default()));
60
61 let UpdatedValues::Buttonlike(buttonlikes) = updated_values else {
62 panic!("Expected Buttonlike, found {updated_values:?}");
63 };
64
65 buttonlikes.insert(Box::new(buttonlike), value);
66 }
67
68 pub fn update_axislike<A: Axislike>(&mut self, axislike: A, value: f32) {
70 let updated_values = self
71 .updated_values
72 .entry(TypeId::of::<A>())
73 .or_insert_with(|| UpdatedValues::Axislike(HashMap::default()));
74
75 let UpdatedValues::Axislike(axislikes) = updated_values else {
76 panic!("Expected Axislike, found {updated_values:?}");
77 };
78
79 axislikes.insert(Box::new(axislike), value);
80 }
81
82 pub fn update_dualaxislike<D: DualAxislike>(&mut self, dualaxislike: D, value: Vec2) {
84 let updated_values = self
85 .updated_values
86 .entry(TypeId::of::<D>())
87 .or_insert_with(|| UpdatedValues::Dualaxislike(HashMap::default()));
88
89 let UpdatedValues::Dualaxislike(dualaxislikes) = updated_values else {
90 panic!("Expected DualAxislike, found {updated_values:?}");
91 };
92
93 dualaxislikes.insert(Box::new(dualaxislike), value);
94 }
95
96 pub fn update_tripleaxislike<T: TripleAxislike>(&mut self, tripleaxislike: T, value: Vec3) {
98 let updated_values = self
99 .updated_values
100 .entry(TypeId::of::<T>())
101 .or_insert_with(|| UpdatedValues::Tripleaxislike(HashMap::default()));
102
103 let UpdatedValues::Tripleaxislike(tripleaxislikes) = updated_values else {
104 panic!("Expected TripleAxislike, found {updated_values:?}");
105 };
106
107 tripleaxislikes.insert(Box::new(tripleaxislike), value);
108 }
109
110 pub fn pressed<B: Buttonlike + Hash + Eq + Clone>(&self, buttonlike: &B) -> bool {
112 let Some(updated_values) = self.updated_values.get(&TypeId::of::<B>()) else {
113 return false;
114 };
115
116 let UpdatedValues::Buttonlike(buttonlikes) = updated_values else {
117 panic!("Expected Buttonlike, found {updated_values:?}");
118 };
119
120 let boxed_buttonlike: Box<dyn Buttonlike> = Box::new(buttonlike.clone());
122
123 buttonlikes
124 .get(&boxed_buttonlike)
125 .copied()
126 .map(|button| button.pressed)
127 .unwrap_or(false)
128 }
129
130 pub fn button_value<B: Buttonlike + Hash + Eq + Clone>(&self, buttonlike: &B) -> f32 {
134 let Some(updated_values) = self.updated_values.get(&TypeId::of::<B>()) else {
135 return 0.0;
136 };
137
138 let UpdatedValues::Buttonlike(buttonlikes) = updated_values else {
139 panic!("Expected Buttonlike, found {updated_values:?}");
140 };
141
142 let boxed_buttonlike: Box<dyn Buttonlike> = Box::new(buttonlike.clone());
144
145 buttonlikes
146 .get(&boxed_buttonlike)
147 .copied()
148 .map(|button| button.value)
149 .unwrap_or(0.0)
150 }
151
152 pub fn value<A: Axislike + Hash + Eq + Clone>(&self, axislike: &A) -> f32 {
156 let Some(updated_values) = self.updated_values.get(&TypeId::of::<A>()) else {
157 return 0.0;
158 };
159
160 let UpdatedValues::Axislike(axislikes) = updated_values else {
161 panic!("Expected Axislike, found {updated_values:?}");
162 };
163
164 let boxed_axislike: Box<dyn Axislike> = Box::new(axislike.clone());
166
167 axislikes.get(&boxed_axislike).copied().unwrap_or(0.0)
168 }
169
170 pub fn pair<D: DualAxislike + Hash + Eq + Clone>(&self, dualaxislike: &D) -> Vec2 {
172 let Some(updated_values) = self.updated_values.get(&TypeId::of::<D>()) else {
173 return Vec2::ZERO;
174 };
175
176 let UpdatedValues::Dualaxislike(dualaxislikes) = updated_values else {
177 panic!("Expected DualAxislike, found {updated_values:?}");
178 };
179
180 let boxed_dualaxislike: Box<dyn DualAxislike> = Box::new(dualaxislike.clone());
182
183 dualaxislikes
184 .get(&boxed_dualaxislike)
185 .copied()
186 .unwrap_or(Vec2::ZERO)
187 }
188
189 pub fn triple<T: TripleAxislike + Hash + Eq + Clone>(&self, tripleaxislike: &T) -> Vec3 {
191 let Some(updated_values) = self.updated_values.get(&TypeId::of::<T>()) else {
192 return Vec3::ZERO;
193 };
194
195 let UpdatedValues::Tripleaxislike(tripleaxislikes) = updated_values else {
196 panic!("Expected TripleAxislike, found {updated_values:?}");
197 };
198
199 let boxed_tripleaxislike: Box<dyn TripleAxislike> = Box::new(tripleaxislike.clone());
201
202 tripleaxislikes
203 .get(&boxed_tripleaxislike)
204 .copied()
205 .unwrap_or(Vec3::ZERO)
206 }
207}
208
209#[derive(Resource)]
210pub struct EnabledInput<T> {
213 pub is_enabled: bool,
215 _p: PhantomData<T>,
216}
217
218unsafe impl<T> Send for EnabledInput<T> {}
221unsafe impl<T> Sync for EnabledInput<T> {}
222
223impl<T: UpdatableInput> Default for EnabledInput<T> {
224 fn default() -> Self {
225 Self {
226 is_enabled: true,
227 _p: PhantomData,
228 }
229 }
230}
231
232pub trait InputRegistration {
234 fn register_input_kind<I: UpdatableInput>(&mut self, kind: InputControlKind);
244}
245
246impl InputRegistration for App {
247 fn register_input_kind<I: UpdatableInput>(&mut self, kind: InputControlKind) {
248 let mut central_input_store = self.world_mut().resource_mut::<CentralInputStore>();
249
250 if central_input_store
252 .registered_input_kinds
253 .contains(&TypeId::of::<I>())
254 {
255 return;
256 }
257
258 central_input_store.updated_values.insert(
259 TypeId::of::<I>(),
260 UpdatedValues::from_input_control_kind(kind),
261 );
262 central_input_store
263 .registered_input_kinds
264 .insert(TypeId::of::<I>());
265 self.insert_resource(EnabledInput::<I>::default());
266 self.add_systems(
267 PreUpdate,
268 I::compute
269 .in_set(InputManagerSystem::Unify)
270 .run_if(input_is_enabled::<I>),
271 );
272 }
273}
274
275pub(crate) fn input_is_enabled<T: UpdatableInput>(enabled_input: Res<EnabledInput<T>>) -> bool {
276 enabled_input.is_enabled
277}
278
279#[allow(unused_variables)]
284pub(crate) fn register_standard_input_kinds(app: &mut App) {
285 #[cfg(feature = "keyboard")]
287 app.register_input_kind::<bevy::input::keyboard::KeyCode>(InputControlKind::Button);
288 #[cfg(feature = "mouse")]
289 app.register_input_kind::<bevy::input::mouse::MouseButton>(InputControlKind::Button);
290 #[cfg(feature = "gamepad")]
291 app.register_input_kind::<bevy::input::gamepad::GamepadButton>(InputControlKind::Button);
292
293 #[cfg(feature = "gamepad")]
295 app.register_input_kind::<bevy::input::gamepad::GamepadAxis>(InputControlKind::Axis);
296
297 #[cfg(feature = "mouse")]
299 app.register_input_kind::<crate::prelude::MouseMove>(InputControlKind::DualAxis);
300 #[cfg(feature = "mouse")]
301 app.register_input_kind::<crate::prelude::MouseScroll>(InputControlKind::DualAxis);
302}
303
304#[derive(Debug, Reflect)]
309enum UpdatedValues {
310 Buttonlike(HashMap<Box<dyn Buttonlike>, ButtonValue>),
311 Axislike(HashMap<Box<dyn Axislike>, f32>),
312 Dualaxislike(HashMap<Box<dyn DualAxislike>, Vec2>),
313 Tripleaxislike(HashMap<Box<dyn TripleAxislike>, Vec3>),
314}
315
316impl UpdatedValues {
317 fn from_input_control_kind(kind: InputControlKind) -> Self {
318 match kind {
319 InputControlKind::Button => Self::Buttonlike(HashMap::default()),
320 InputControlKind::Axis => Self::Axislike(HashMap::default()),
321 InputControlKind::DualAxis => Self::Dualaxislike(HashMap::default()),
322 InputControlKind::TripleAxis => Self::Tripleaxislike(HashMap::default()),
323 }
324 }
325}
326
327pub trait UpdatableInput: 'static {
342 type SourceData: SystemParam;
348
349 fn compute(
357 central_input_store: ResMut<CentralInputStore>,
358 source_data: StaticSystemParam<Self::SourceData>,
359 );
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365 use leafwing_input_manager_macros::Actionlike;
366
367 use crate as leafwing_input_manager;
368 use crate::plugin::{CentralInputStorePlugin, InputManagerPlugin};
369
370 #[derive(Actionlike, Debug, PartialEq, Eq, Hash, Clone, Reflect)]
371 enum TestAction {
372 Run,
373 Jump,
374 }
375
376 #[test]
377 fn central_input_store_is_added_by_plugins() {
378 let mut app = App::new();
379 app.add_plugins(CentralInputStorePlugin);
380 assert!(app.world().contains_resource::<CentralInputStore>());
381
382 let mut app = App::new();
383 app.add_plugins(InputManagerPlugin::<TestAction>::default());
384 assert!(app.world().contains_resource::<CentralInputStore>());
385 }
386
387 #[test]
388 fn number_of_maps_matches_number_of_registered_input_kinds() {
389 let mut app = App::new();
390 app.add_plugins(CentralInputStorePlugin);
391 let central_input_store = app.world().resource::<CentralInputStore>();
392
393 assert_eq!(
394 central_input_store.updated_values.len(),
395 central_input_store.registered_input_kinds.len()
396 );
397 }
398
399 #[cfg(feature = "mouse")]
400 #[test]
401 fn compute_call_updates_central_store() {
402 use bevy::ecs::system::RunSystemOnce;
403 use bevy::prelude::*;
404
405 let mut world = World::new();
406 world.init_resource::<CentralInputStore>();
407
408 let mut mouse_button_input = ButtonInput::<MouseButton>::default();
410 mouse_button_input.press(MouseButton::Left);
411 assert!(mouse_button_input.pressed(MouseButton::Left));
412 dbg!(&mouse_button_input);
413 world.insert_resource(mouse_button_input);
414
415 world.run_system_once(MouseButton::compute).unwrap();
416 let central_input_store = world.resource::<CentralInputStore>();
417 dbg!(central_input_store);
418 assert!(central_input_store.pressed(&MouseButton::Left));
419 }
420}