1use std::fmt::Debug;
4use std::hash::Hash;
5
6#[cfg(feature = "asset")]
7use bevy::asset::Asset;
8use bevy::input::gamepad::Gamepad;
9use bevy::platform::collections::HashMap;
10use bevy::prelude::{Component, Deref, DerefMut, Entity, Query, Reflect, Resource, With};
11use bevy::{log::error, prelude::ReflectComponent};
12use bevy::{
13 math::{Vec2, Vec3},
14 prelude::ReflectResource,
15};
16use itertools::Itertools;
17use serde::{Deserialize, Serialize};
18
19use crate::buttonlike::ButtonValue;
20use crate::clashing_inputs::ClashStrategy;
21use crate::prelude::updating::CentralInputStore;
22use crate::prelude::{ActionState, UserInputWrapper};
23use crate::user_input::{Axislike, Buttonlike, DualAxislike, TripleAxislike};
24use crate::{Actionlike, InputControlKind};
25
26#[cfg(feature = "gamepad")]
27use crate::user_input::gamepad::find_gamepad;
28
29#[cfg(not(feature = "gamepad"))]
30fn find_gamepad(_: Option<Query<Entity, With<Gamepad>>>) -> Entity {
31 Entity::PLACEHOLDER
32}
33
34#[derive(Resource, Component, Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
107#[require(ActionState::<A>)]
108#[cfg_attr(feature = "asset", derive(Asset))]
109#[reflect(Resource, Component)]
110pub struct InputMap<A: Actionlike> {
111 buttonlike_map: HashMap<A, Vec<Box<dyn Buttonlike>>>,
113
114 axislike_map: HashMap<A, Vec<Box<dyn Axislike>>>,
116
117 dual_axislike_map: HashMap<A, Vec<Box<dyn DualAxislike>>>,
119
120 triple_axislike_map: HashMap<A, Vec<Box<dyn TripleAxislike>>>,
122
123 associated_gamepad: Option<Entity>,
125}
126
127impl<A: Actionlike> Default for InputMap<A> {
128 fn default() -> Self {
129 InputMap {
130 buttonlike_map: HashMap::default(),
131 axislike_map: HashMap::default(),
132 dual_axislike_map: HashMap::default(),
133 triple_axislike_map: HashMap::default(),
134 associated_gamepad: None,
135 }
136 }
137}
138
139impl<A: Actionlike> InputMap<A> {
141 #[inline(always)]
147 pub fn new(bindings: impl IntoIterator<Item = (A, impl Buttonlike)>) -> Self {
148 bindings
149 .into_iter()
150 .fold(Self::default(), |map, (action, input)| {
151 map.with(action, input)
152 })
153 }
154
155 #[inline(always)]
161 pub fn with(mut self, action: A, button: impl Buttonlike) -> Self {
162 self.insert(action, button);
163 self
164 }
165
166 #[inline(always)]
172 pub fn with_axis(mut self, action: A, axis: impl Axislike) -> Self {
173 self.insert_axis(action, axis);
174 self
175 }
176
177 #[inline(always)]
183 pub fn with_dual_axis(mut self, action: A, dual_axis: impl DualAxislike) -> Self {
184 self.insert_dual_axis(action, dual_axis);
185 self
186 }
187
188 #[inline(always)]
194 pub fn with_triple_axis(mut self, action: A, triple_axis: impl TripleAxislike) -> Self {
195 self.insert_triple_axis(action, triple_axis);
196 self
197 }
198
199 #[inline(always)]
205 pub fn with_one_to_many(
206 mut self,
207 action: A,
208 inputs: impl IntoIterator<Item = impl Buttonlike>,
209 ) -> Self {
210 self.insert_one_to_many(action, inputs);
211 self
212 }
213
214 #[inline(always)]
220 pub fn with_multiple(
221 mut self,
222 bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
223 ) -> Self {
224 self.insert_multiple(bindings);
225 self
226 }
227}
228
229#[inline(always)]
230fn insert_unique<K, V>(map: &mut HashMap<K, Vec<V>>, key: &K, value: V)
231where
232 K: Clone + Eq + Hash,
233 V: PartialEq,
234{
235 if let Some(list) = map.get_mut(key) {
236 if !list.contains(&value) {
237 list.push(value);
238 }
239 } else {
240 map.insert(key.clone(), vec![value]);
241 }
242}
243
244impl<A: Actionlike> InputMap<A> {
246 #[inline(always)]
252 #[track_caller]
253 pub fn insert(&mut self, action: A, button: impl Buttonlike) -> &mut Self {
254 self.insert_boxed(action, Box::new(button));
255 self
256 }
257
258 #[inline(always)]
263 #[track_caller]
264 pub fn insert_boxed(&mut self, action: A, button: Box<dyn Buttonlike>) -> &mut Self {
265 debug_assert!(
266 action.input_control_kind() == InputControlKind::Button,
267 "Cannot map a Buttonlike input for action {:?} of kind {:?}",
268 action,
269 action.input_control_kind()
270 );
271
272 if action.input_control_kind() != InputControlKind::Button {
273 error!(
274 "Cannot map a Buttonlike input for action {:?} of kind {:?}",
275 action,
276 action.input_control_kind()
277 );
278
279 return self;
280 }
281
282 insert_unique(&mut self.buttonlike_map, &action, button);
283 self
284 }
285
286 #[inline(always)]
292 #[track_caller]
293 pub fn insert_axis(&mut self, action: A, axis: impl Axislike) -> &mut Self {
294 self.insert_axis_boxed(action, Box::new(axis));
295 self
296 }
297
298 #[inline(always)]
303 #[track_caller]
304 pub fn insert_axis_boxed(&mut self, action: A, axis: Box<dyn Axislike>) -> &mut Self {
305 debug_assert!(
306 action.input_control_kind() == InputControlKind::Axis,
307 "Cannot map an Axislike input for action {:?} of kind {:?}",
308 action,
309 action.input_control_kind()
310 );
311
312 if action.input_control_kind() != InputControlKind::Axis {
313 error!(
314 "Cannot map an Axislike input for action {:?} of kind {:?}",
315 action,
316 action.input_control_kind()
317 );
318
319 return self;
320 }
321
322 insert_unique(&mut self.axislike_map, &action, axis);
323 self
324 }
325
326 #[inline(always)]
332 #[track_caller]
333 pub fn insert_dual_axis(&mut self, action: A, dual_axis: impl DualAxislike) -> &mut Self {
334 self.insert_dual_axis_boxed(action, Box::new(dual_axis));
335 self
336 }
337
338 #[inline(always)]
343 #[track_caller]
344 pub fn insert_dual_axis_boxed(&mut self, action: A, axis: Box<dyn DualAxislike>) -> &mut Self {
345 debug_assert!(
346 action.input_control_kind() == InputControlKind::DualAxis,
347 "Cannot map a DualAxislike input for action {:?} of kind {:?}",
348 action,
349 action.input_control_kind()
350 );
351
352 if action.input_control_kind() != InputControlKind::DualAxis {
353 error!(
354 "Cannot map a DualAxislike input for action {:?} of kind {:?}",
355 action,
356 action.input_control_kind()
357 );
358
359 return self;
360 }
361
362 insert_unique(&mut self.dual_axislike_map, &action, axis);
363 self
364 }
365
366 #[inline(always)]
372 #[track_caller]
373 pub fn insert_triple_axis(&mut self, action: A, triple_axis: impl TripleAxislike) -> &mut Self {
374 self.insert_triple_axis_boxed(action, Box::new(triple_axis));
375 self
376 }
377
378 #[inline(always)]
383 #[track_caller]
384 pub fn insert_triple_axis_boxed(
385 &mut self,
386 action: A,
387 triple_axis: Box<dyn TripleAxislike>,
388 ) -> &mut Self {
389 debug_assert!(
390 action.input_control_kind() == InputControlKind::TripleAxis,
391 "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
392 action,
393 action.input_control_kind()
394 );
395
396 if action.input_control_kind() != InputControlKind::TripleAxis {
397 error!(
398 "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
399 action,
400 action.input_control_kind()
401 );
402
403 return self;
404 }
405
406 insert_unique(&mut self.triple_axislike_map, &action, triple_axis);
407 self
408 }
409
410 #[inline(always)]
418 pub fn insert_one_to_many(
419 &mut self,
420 action: A,
421 inputs: impl IntoIterator<Item = impl Buttonlike>,
422 ) -> &mut Self {
423 let inputs = inputs
424 .into_iter()
425 .map(|input| Box::new(input) as Box<dyn Buttonlike>);
426 self.insert_one_to_many_boxed(action, inputs);
427 self
428 }
429
430 #[inline(always)]
435 pub fn insert_one_to_many_boxed(
436 &mut self,
437 action: A,
438 inputs: impl IntoIterator<Item = Box<dyn Buttonlike>>,
439 ) -> &mut Self {
440 let inputs = inputs.into_iter();
441 if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
442 for input in inputs {
443 if !bindings.contains(&input) {
444 bindings.push(input);
445 }
446 }
447 } else {
448 self.buttonlike_map
449 .insert(action, inputs.unique().collect());
450 }
451 self
452 }
453
454 #[inline(always)]
460 pub fn insert_multiple(
461 &mut self,
462 bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
463 ) -> &mut Self {
464 for (action, input) in bindings.into_iter() {
465 self.insert(action, input);
466 }
467 self
468 }
469
470 #[inline(always)]
475 pub fn insert_multiple_boxed(
476 &mut self,
477 bindings: impl IntoIterator<Item = (A, Box<dyn Buttonlike>)>,
478 ) -> &mut Self {
479 for (action, input) in bindings.into_iter() {
480 self.insert_boxed(action, input);
481 }
482 self
483 }
484
485 pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
490 if self.associated_gamepad != other.associated_gamepad {
491 self.clear_gamepad();
492 }
493
494 for (other_action, other_inputs) in other.iter_buttonlike() {
495 for other_input in other_inputs.iter().cloned() {
496 insert_unique(&mut self.buttonlike_map, other_action, other_input);
497 }
498 }
499
500 for (other_action, other_inputs) in other.iter_axislike() {
501 for other_input in other_inputs.iter().cloned() {
502 insert_unique(&mut self.axislike_map, other_action, other_input);
503 }
504 }
505
506 for (other_action, other_inputs) in other.iter_dual_axislike() {
507 for other_input in other_inputs.iter().cloned() {
508 insert_unique(&mut self.dual_axislike_map, other_action, other_input);
509 }
510 }
511
512 for (other_action, other_inputs) in other.iter_triple_axislike() {
513 for other_input in other_inputs.iter().cloned() {
514 insert_unique(&mut self.triple_axislike_map, other_action, other_input);
515 }
516 }
517
518 self
519 }
520}
521
522impl<A: Actionlike> InputMap<A> {
524 #[must_use]
528 #[inline]
529 pub const fn gamepad(&self) -> Option<Entity> {
530 self.associated_gamepad
531 }
532
533 #[inline]
545 pub fn with_gamepad(mut self, gamepad: Entity) -> Self {
546 self.set_gamepad(gamepad);
547 self
548 }
549
550 #[inline]
562 pub fn set_gamepad(&mut self, gamepad: Entity) -> &mut Self {
563 self.associated_gamepad = Some(gamepad);
564 self
565 }
566
567 #[inline]
569 pub fn clear_gamepad(&mut self) -> &mut Self {
570 self.associated_gamepad = None;
571 self
572 }
573}
574
575impl<A: Actionlike> InputMap<A> {
577 #[must_use]
581 pub fn pressed(
582 &self,
583 action: &A,
584 input_store: &CentralInputStore,
585 clash_strategy: ClashStrategy,
586 ) -> bool {
587 let processed_actions = self.process_actions(None, input_store, clash_strategy);
588
589 let Some(updated_value) = processed_actions.get(action) else {
590 return false;
591 };
592
593 match updated_value {
594 UpdatedValue::Button(ButtonValue { pressed, value: _ }) => *pressed,
595 _ => false,
596 }
597 }
598
599 #[must_use]
610 pub fn process_actions(
611 &self,
612 gamepads: Option<Query<Entity, With<Gamepad>>>,
613 input_store: &CentralInputStore,
614 clash_strategy: ClashStrategy,
615 ) -> UpdatedActions<A> {
616 let mut updated_actions = UpdatedActions::default();
617 let gamepad = self.associated_gamepad.unwrap_or(find_gamepad(gamepads));
618
619 for (action, input_bindings) in self.iter_buttonlike() {
621 let mut final_state: Option<(bool, f32)> = None;
622 for binding in input_bindings {
623 let pressed = binding.pressed(input_store, gamepad);
624 let value = binding.value(input_store, gamepad);
625 if let Some((existing_state, existing_value)) = final_state {
626 final_state = Some((existing_state || pressed, existing_value.max(value)));
627 } else {
628 final_state = Some((pressed, value));
629 }
630 }
631
632 if let Some((pressed, value)) = final_state {
633 updated_actions.insert(
634 action.clone(),
635 UpdatedValue::Button(ButtonValue { pressed, value }),
636 );
637 }
638 }
639
640 for (action, input_bindings) in self.iter_axislike() {
641 let mut final_value = None;
642 for binding in input_bindings {
643 if let Some(value) = binding.get_value(input_store, gamepad) {
644 final_value = Some(final_value.unwrap_or(0.0) + value);
645 }
646 }
647
648 if let Some(final_value) = final_value {
649 updated_actions.insert(action.clone(), UpdatedValue::Axis(final_value));
650 }
651 }
652
653 for (action, _input_bindings) in self.iter_dual_axislike() {
654 let mut final_value = None;
655 for binding in _input_bindings {
656 if let Some(axis_pair) = binding.get_axis_pair(input_store, gamepad) {
657 final_value = Some(final_value.unwrap_or(Vec2::ZERO) + axis_pair);
658 }
659 }
660
661 if let Some(final_value) = final_value {
662 updated_actions.insert(action.clone(), UpdatedValue::DualAxis(final_value));
663 }
664 }
665
666 for (action, _input_bindings) in self.iter_triple_axislike() {
667 let mut final_value = Vec3::ZERO;
668 for binding in _input_bindings {
669 final_value += binding.axis_triple(input_store, gamepad);
670 }
671
672 updated_actions.insert(action.clone(), UpdatedValue::TripleAxis(final_value));
673 }
674
675 self.handle_clashes(&mut updated_actions, input_store, clash_strategy, gamepad);
677
678 updated_actions
679 }
680}
681
682#[derive(Debug, Clone, PartialEq, Deref, DerefMut)]
685pub struct UpdatedActions<A: Actionlike>(pub HashMap<A, UpdatedValue>);
686
687impl<A: Actionlike> UpdatedActions<A> {
688 pub fn pressed(&self, action: &A) -> bool {
690 match self.0.get(action) {
691 Some(UpdatedValue::Button(ButtonValue { pressed, value: _ })) => *pressed,
692 _ => false,
693 }
694 }
695}
696
697#[derive(Debug, Clone, Copy, PartialEq)]
701pub enum UpdatedValue {
702 Button(ButtonValue),
704 Axis(f32),
706 DualAxis(Vec2),
708 TripleAxis(Vec3),
710}
711
712impl<A: Actionlike> Default for UpdatedActions<A> {
713 fn default() -> Self {
714 Self(HashMap::default())
715 }
716}
717
718impl<A: Actionlike> InputMap<A> {
720 pub fn iter_buttonlike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Buttonlike>>)> {
722 self.buttonlike_map.iter()
723 }
724
725 pub fn iter_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Axislike>>)> {
727 self.axislike_map.iter()
728 }
729
730 pub fn iter_dual_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn DualAxislike>>)> {
732 self.dual_axislike_map.iter()
733 }
734
735 pub fn iter_triple_axislike(
737 &self,
738 ) -> impl Iterator<Item = (&A, &Vec<Box<dyn TripleAxislike>>)> {
739 self.triple_axislike_map.iter()
740 }
741
742 pub fn buttonlike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Buttonlike)> {
744 self.buttonlike_map
745 .iter()
746 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
747 }
748
749 pub fn axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Axislike)> {
751 self.axislike_map
752 .iter()
753 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
754 }
755
756 pub fn dual_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn DualAxislike)> {
758 self.dual_axislike_map
759 .iter()
760 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
761 }
762
763 pub fn triple_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn TripleAxislike)> {
765 self.triple_axislike_map
766 .iter()
767 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
768 }
769
770 pub fn buttonlike_actions(&self) -> impl Iterator<Item = &A> {
772 self.buttonlike_map.keys()
773 }
774
775 pub fn axislike_actions(&self) -> impl Iterator<Item = &A> {
777 self.axislike_map.keys()
778 }
779
780 pub fn dual_axislike_actions(&self) -> impl Iterator<Item = &A> {
782 self.dual_axislike_map.keys()
783 }
784
785 pub fn triple_axislike_actions(&self) -> impl Iterator<Item = &A> {
787 self.triple_axislike_map.keys()
788 }
789
790 #[must_use]
799 pub fn get(&self, action: &A) -> Option<Vec<UserInputWrapper>> {
800 match action.input_control_kind() {
801 InputControlKind::Button => {
802 let buttonlike = self.buttonlike_map.get(action)?;
803 Some(
804 buttonlike
805 .iter()
806 .map(|input| UserInputWrapper::Button(input.clone()))
807 .collect(),
808 )
809 }
810 InputControlKind::Axis => {
811 let axislike = self.axislike_map.get(action)?;
812 Some(
813 axislike
814 .iter()
815 .map(|input| UserInputWrapper::Axis(input.clone()))
816 .collect(),
817 )
818 }
819 InputControlKind::DualAxis => {
820 let dual_axislike = self.dual_axislike_map.get(action)?;
821 Some(
822 dual_axislike
823 .iter()
824 .map(|input| UserInputWrapper::DualAxis(input.clone()))
825 .collect(),
826 )
827 }
828 InputControlKind::TripleAxis => {
829 let triple_axislike = self.triple_axislike_map.get(action)?;
830 Some(
831 triple_axislike
832 .iter()
833 .map(|input| UserInputWrapper::TripleAxis(input.clone()))
834 .collect(),
835 )
836 }
837 }
838 }
839
840 #[must_use]
842 pub fn get_buttonlike(&self, action: &A) -> Option<&Vec<Box<dyn Buttonlike>>> {
843 self.buttonlike_map.get(action)
844 }
845
846 #[must_use]
848 pub fn get_buttonlike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Buttonlike>>> {
849 self.buttonlike_map.get_mut(action)
850 }
851
852 #[must_use]
854 pub fn get_axislike(&self, action: &A) -> Option<&Vec<Box<dyn Axislike>>> {
855 self.axislike_map.get(action)
856 }
857
858 #[must_use]
860 pub fn get_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Axislike>>> {
861 self.axislike_map.get_mut(action)
862 }
863
864 #[must_use]
866 pub fn get_dual_axislike(&self, action: &A) -> Option<&Vec<Box<dyn DualAxislike>>> {
867 self.dual_axislike_map.get(action)
868 }
869
870 #[must_use]
872 pub fn get_dual_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn DualAxislike>>> {
873 self.dual_axislike_map.get_mut(action)
874 }
875
876 #[must_use]
878 pub fn get_triple_axislike(&self, action: &A) -> Option<&Vec<Box<dyn TripleAxislike>>> {
879 self.triple_axislike_map.get(action)
880 }
881
882 #[must_use]
884 pub fn get_triple_axislike_mut(
885 &mut self,
886 action: &A,
887 ) -> Option<&mut Vec<Box<dyn TripleAxislike>>> {
888 self.triple_axislike_map.get_mut(action)
889 }
890
891 #[must_use]
893 pub fn len(&self) -> usize {
894 self.buttonlike_map.values().map(Vec::len).sum::<usize>()
895 + self.axislike_map.values().map(Vec::len).sum::<usize>()
896 + self.dual_axislike_map.values().map(Vec::len).sum::<usize>()
897 + self
898 .triple_axislike_map
899 .values()
900 .map(Vec::len)
901 .sum::<usize>()
902 }
903
904 #[inline]
906 #[must_use]
907 pub fn is_empty(&self) -> bool {
908 self.len() == 0
909 }
910
911 pub fn clear(&mut self) {
913 self.buttonlike_map.clear();
914 self.axislike_map.clear();
915 self.dual_axislike_map.clear();
916 self.triple_axislike_map.clear();
917 }
918}
919
920impl<A: Actionlike> InputMap<A> {
922 pub fn clear_action(&mut self, action: &A) {
924 match action.input_control_kind() {
925 InputControlKind::Button => {
926 self.buttonlike_map.remove(action);
927 }
928 InputControlKind::Axis => {
929 self.axislike_map.remove(action);
930 }
931 InputControlKind::DualAxis => {
932 self.dual_axislike_map.remove(action);
933 }
934 InputControlKind::TripleAxis => {
935 self.triple_axislike_map.remove(action);
936 }
937 }
938 }
939
940 pub fn remove_at(&mut self, action: &A, index: usize) -> Option<()> {
948 match action.input_control_kind() {
949 InputControlKind::Button => {
950 let input_bindings = self.buttonlike_map.get_mut(action)?;
951 if input_bindings.len() > index {
952 input_bindings.remove(index);
953 Some(())
954 } else {
955 None
956 }
957 }
958 InputControlKind::Axis => {
959 let input_bindings = self.axislike_map.get_mut(action)?;
960 if input_bindings.len() > index {
961 input_bindings.remove(index);
962 Some(())
963 } else {
964 None
965 }
966 }
967 InputControlKind::DualAxis => {
968 let input_bindings = self.dual_axislike_map.get_mut(action)?;
969 if input_bindings.len() > index {
970 input_bindings.remove(index);
971 Some(())
972 } else {
973 None
974 }
975 }
976 InputControlKind::TripleAxis => {
977 let input_bindings = self.triple_axislike_map.get_mut(action)?;
978 if input_bindings.len() > index {
979 input_bindings.remove(index);
980 Some(())
981 } else {
982 None
983 }
984 }
985 }
986 }
987
988 pub fn remove(&mut self, action: &A, input: impl Buttonlike) -> Option<usize> {
992 let bindings = self.buttonlike_map.get_mut(action)?;
993 let boxed_input: Box<dyn Buttonlike> = Box::new(input);
994 let index = bindings.iter().position(|input| input == &boxed_input)?;
995 bindings.remove(index);
996 Some(index)
997 }
998}
999
1000impl<A: Actionlike, U: Buttonlike> From<HashMap<A, Vec<U>>> for InputMap<A> {
1001 fn from(raw_map: HashMap<A, Vec<U>>) -> Self {
1028 let mut input_map = Self::default();
1029 for (action, inputs) in raw_map.into_iter() {
1030 input_map.insert_one_to_many(action, inputs);
1031 }
1032 input_map
1033 }
1034}
1035
1036impl<A: Actionlike, U: Buttonlike> FromIterator<(A, U)> for InputMap<A> {
1037 fn from_iter<T: IntoIterator<Item = (A, U)>>(iter: T) -> Self {
1038 let mut input_map = Self::default();
1039 for (action, input) in iter.into_iter() {
1040 input_map.insert(action, input);
1041 }
1042 input_map
1043 }
1044}
1045
1046#[cfg(test)]
1047#[cfg(feature = "keyboard")]
1048mod tests {
1049 use bevy::prelude::Reflect;
1050 use serde::{Deserialize, Serialize};
1051
1052 use super::*;
1053 use crate as leafwing_input_manager;
1054 use crate::prelude::*;
1055
1056 #[derive(Actionlike, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug, Reflect)]
1057 enum Action {
1058 Run,
1059 Jump,
1060 Hide,
1061 #[actionlike(Axis)]
1062 Axis,
1063 #[actionlike(DualAxis)]
1064 DualAxis,
1065 #[actionlike(TripleAxis)]
1066 TripleAxis,
1067 }
1068
1069 #[test]
1070 fn creation() {
1071 use bevy::input::keyboard::KeyCode;
1072
1073 let input_map = InputMap::default()
1074 .with(Action::Run, KeyCode::KeyW)
1075 .with(Action::Run, KeyCode::ShiftLeft)
1076 .with(Action::Run, KeyCode::ShiftLeft)
1078 .with_one_to_many(Action::Run, [KeyCode::KeyR, KeyCode::ShiftRight])
1079 .with_multiple([
1080 (Action::Jump, KeyCode::Space),
1081 (Action::Hide, KeyCode::ControlLeft),
1082 (Action::Hide, KeyCode::ControlRight),
1083 ]);
1084
1085 let expected_bindings: HashMap<Box<dyn Buttonlike>, Action> = [
1086 (Box::new(KeyCode::KeyW) as Box<dyn Buttonlike>, Action::Run),
1087 (
1088 Box::new(KeyCode::ShiftLeft) as Box<dyn Buttonlike>,
1089 Action::Run,
1090 ),
1091 (Box::new(KeyCode::KeyR) as Box<dyn Buttonlike>, Action::Run),
1092 (
1093 Box::new(KeyCode::ShiftRight) as Box<dyn Buttonlike>,
1094 Action::Run,
1095 ),
1096 (
1097 Box::new(KeyCode::Space) as Box<dyn Buttonlike>,
1098 Action::Jump,
1099 ),
1100 (
1101 Box::new(KeyCode::ControlLeft) as Box<dyn Buttonlike>,
1102 Action::Hide,
1103 ),
1104 (
1105 Box::new(KeyCode::ControlRight) as Box<dyn Buttonlike>,
1106 Action::Hide,
1107 ),
1108 ]
1109 .into_iter()
1110 .collect();
1111
1112 for (action, input) in input_map.buttonlike_bindings() {
1113 let expected_action = expected_bindings.get(input).unwrap();
1114 assert_eq!(expected_action, action);
1115 }
1116 }
1117
1118 #[test]
1119 fn insertion_idempotency() {
1120 use bevy::input::keyboard::KeyCode;
1121
1122 let mut input_map = InputMap::default();
1123 input_map.insert(Action::Run, KeyCode::Space);
1124
1125 let expected: Vec<Box<dyn Buttonlike>> = vec![Box::new(KeyCode::Space)];
1126 assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1127
1128 input_map.insert(Action::Run, KeyCode::Space);
1130 assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1131 }
1132
1133 #[test]
1134 fn multiple_insertion() {
1135 use bevy::input::keyboard::KeyCode;
1136
1137 let mut input_map = InputMap::default();
1138 input_map.insert(Action::Run, KeyCode::Space);
1139 input_map.insert(Action::Run, KeyCode::Enter);
1140
1141 let expected: Vec<Box<dyn Buttonlike>> =
1142 vec![Box::new(KeyCode::Space), Box::new(KeyCode::Enter)];
1143 assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1144 }
1145
1146 #[test]
1147 fn input_clearing() {
1148 use bevy::input::keyboard::KeyCode;
1149
1150 let mut input_map = InputMap::default();
1151 input_map.insert(Action::Run, KeyCode::Space);
1152
1153 input_map.clear_action(&Action::Run);
1155 assert_eq!(input_map, InputMap::default());
1156
1157 input_map.insert(Action::Run, KeyCode::Space);
1159 input_map.insert(Action::Run, KeyCode::ShiftLeft);
1160 assert!(input_map.remove_at(&Action::Run, 1).is_some());
1161 assert!(
1162 input_map.remove_at(&Action::Run, 1).is_none(),
1163 "Should return None on second removal at the same index"
1164 );
1165 assert!(input_map.remove_at(&Action::Run, 0).is_some());
1166 assert!(
1167 input_map.remove_at(&Action::Run, 0).is_none(),
1168 "Should return None on second removal at the same index"
1169 );
1170 }
1171
1172 #[test]
1173 fn merging() {
1174 use bevy::input::keyboard::KeyCode;
1175
1176 let mut input_map = InputMap::default();
1177 let mut default_keyboard_map = InputMap::default();
1178 default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft);
1179 default_keyboard_map.insert(
1180 Action::Hide,
1181 ButtonlikeChord::new([KeyCode::ControlLeft, KeyCode::KeyH]),
1182 );
1183
1184 let mut default_gamepad_map = InputMap::default();
1185 default_gamepad_map.insert(Action::Run, KeyCode::Numpad0);
1186 default_gamepad_map.insert(Action::Hide, KeyCode::Numpad7);
1187
1188 input_map.merge(&default_keyboard_map);
1190 assert_eq!(input_map, default_keyboard_map);
1191
1192 input_map.merge(&default_keyboard_map);
1194 assert_eq!(input_map, default_keyboard_map);
1195 }
1196
1197 #[cfg(feature = "gamepad")]
1198 #[test]
1199 fn gamepad_swapping() {
1200 use bevy::ecs::world::World;
1201
1202 let mut input_map = InputMap::<Action>::default();
1203 assert_eq!(input_map.gamepad(), None);
1204
1205 let test_entity = World::new().spawn_empty().id();
1206 input_map.set_gamepad(test_entity);
1207 assert_eq!(input_map.gamepad(), Some(test_entity));
1208
1209 input_map.clear_gamepad();
1210 assert_eq!(input_map.gamepad(), None);
1211 }
1212
1213 #[cfg(feature = "keyboard")]
1214 #[test]
1215 fn input_map_serde() {
1216 use bevy::prelude::{App, KeyCode};
1217 use serde_test::{Token, assert_tokens};
1218
1219 let mut app = App::new();
1220
1221 app.add_plugins(InputManagerPlugin::<Action>::default());
1223
1224 let input_map = InputMap::new([(Action::Hide, KeyCode::ControlLeft)]);
1225 assert_tokens(
1226 &input_map,
1227 &[
1228 Token::Struct {
1229 name: "InputMap",
1230 len: 5,
1231 },
1232 Token::Str("buttonlike_map"),
1233 Token::Map { len: Some(1) },
1234 Token::UnitVariant {
1235 name: "Action",
1236 variant: "Hide",
1237 },
1238 Token::Seq { len: Some(1) },
1239 Token::Map { len: Some(1) },
1240 Token::BorrowedStr("KeyCode"),
1241 Token::UnitVariant {
1242 name: "KeyCode",
1243 variant: "ControlLeft",
1244 },
1245 Token::MapEnd,
1246 Token::SeqEnd,
1247 Token::MapEnd,
1248 Token::Str("axislike_map"),
1249 Token::Map { len: Some(0) },
1250 Token::MapEnd,
1251 Token::Str("dual_axislike_map"),
1252 Token::Map { len: Some(0) },
1253 Token::MapEnd,
1254 Token::Str("triple_axislike_map"),
1255 Token::Map { len: Some(0) },
1256 Token::MapEnd,
1257 Token::Str("associated_gamepad"),
1258 Token::None,
1259 Token::StructEnd,
1260 ],
1261 );
1262 }
1263}