Skip to main content

cloudiful_bevy_input/
bindings.rs

1use bevy::input::gamepad::{GamepadAxis, GamepadButton};
2use bevy::input::keyboard::KeyCode;
3use bevy::prelude::Resource;
4use std::collections::HashMap;
5use std::hash::Hash;
6
7pub trait InputAction: Copy + Eq + Hash + Send + Sync + 'static {}
8
9impl<T> InputAction for T where T: Copy + Eq + Hash + Send + Sync + 'static {}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum InputButton {
13    Key(KeyCode),
14    Gamepad(GamepadButton),
15}
16
17#[derive(Debug, Clone, Copy, PartialEq)]
18pub enum InputBinding {
19    Button(InputButton),
20    GamepadAxis(GamepadAxis),
21    ButtonAxis {
22        negative: InputButton,
23        positive: InputButton,
24    },
25}
26
27#[derive(Resource, Debug, Clone)]
28pub struct InputMap<A: InputAction> {
29    bindings: HashMap<A, Vec<InputBinding>>,
30}
31
32impl<A: InputAction> Default for InputMap<A> {
33    fn default() -> Self {
34        Self {
35            bindings: HashMap::new(),
36        }
37    }
38}
39
40impl<A: InputAction> InputMap<A> {
41    pub fn bindings(&self, action: A) -> &[InputBinding] {
42        self.bindings.get(&action).map(Vec::as_slice).unwrap_or(&[])
43    }
44
45    pub fn actions(&self) -> impl Iterator<Item = A> + '_ {
46        self.bindings.keys().copied()
47    }
48
49    pub fn clear_action(&mut self, action: A) {
50        self.bindings.remove(&action);
51    }
52
53    pub fn set_bindings(
54        &mut self,
55        action: A,
56        bindings: impl IntoIterator<Item = InputBinding>,
57    ) -> &mut Self {
58        self.bindings.insert(action, bindings.into_iter().collect());
59        self
60    }
61
62    pub fn bind_key(&mut self, action: A, key: KeyCode) -> &mut Self {
63        self.push(action, InputBinding::Button(InputButton::Key(key)))
64    }
65
66    pub fn bind_gamepad_button(&mut self, action: A, button: GamepadButton) -> &mut Self {
67        self.push(action, InputBinding::Button(InputButton::Gamepad(button)))
68    }
69
70    pub fn bind_gamepad_axis(&mut self, action: A, axis: GamepadAxis) -> &mut Self {
71        self.push(action, InputBinding::GamepadAxis(axis))
72    }
73
74    pub fn bind_button_axis(
75        &mut self,
76        action: A,
77        negative: InputButton,
78        positive: InputButton,
79    ) -> &mut Self {
80        self.push(action, InputBinding::ButtonAxis { negative, positive })
81    }
82
83    pub fn rebind_button(&mut self, action: A, old: InputButton, new: InputButton) -> bool {
84        self.replace(action, |binding| match binding {
85            InputBinding::Button(button) if *button == old => {
86                *binding = InputBinding::Button(new);
87                true
88            }
89            InputBinding::ButtonAxis { negative, positive } if *negative == old => {
90                *binding = InputBinding::ButtonAxis {
91                    negative: new,
92                    positive: *positive,
93                };
94                true
95            }
96            InputBinding::ButtonAxis { negative, positive } if *positive == old => {
97                *binding = InputBinding::ButtonAxis {
98                    negative: *negative,
99                    positive: new,
100                };
101                true
102            }
103            _ => false,
104        })
105    }
106
107    pub fn rebind_gamepad_axis(&mut self, action: A, old: GamepadAxis, new: GamepadAxis) -> bool {
108        self.replace(action, |binding| match binding {
109            InputBinding::GamepadAxis(axis) if *axis == old => {
110                *binding = InputBinding::GamepadAxis(new);
111                true
112            }
113            _ => false,
114        })
115    }
116
117    fn push(&mut self, action: A, binding: InputBinding) -> &mut Self {
118        self.bindings.entry(action).or_default().push(binding);
119        self
120    }
121
122    fn replace(&mut self, action: A, mut replace: impl FnMut(&mut InputBinding) -> bool) -> bool {
123        let Some(bindings) = self.bindings.get_mut(&action) else {
124            return false;
125        };
126
127        for binding in bindings.iter_mut() {
128            if replace(binding) {
129                return true;
130            }
131        }
132
133        false
134    }
135}