use std::fmt::Debug;
#[cfg(feature = "asset")]
use bevy::asset::Asset;
use bevy::log::error;
use bevy::math::Vec2;
use bevy::prelude::{Component, Deref, DerefMut, Gamepad, Gamepads, Reflect, Resource};
use bevy::utils::HashMap;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use crate::clashing_inputs::ClashStrategy;
use crate::prelude::updating::CentralInputStore;
use crate::prelude::UserInputWrapper;
use crate::user_input::{Axislike, Buttonlike, DualAxislike};
use crate::{Actionlike, InputControlKind};
#[cfg(feature = "gamepad")]
use crate::user_input::gamepad::find_gamepad;
#[cfg(not(feature = "gamepad"))]
fn find_gamepad(_gamepads: &Gamepads) -> Gamepad {
Gamepad::new(0)
}
#[derive(Resource, Component, Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
#[cfg_attr(feature = "asset", derive(Asset))]
pub struct InputMap<A: Actionlike> {
buttonlike_map: HashMap<A, Vec<Box<dyn Buttonlike>>>,
axislike_map: HashMap<A, Vec<Box<dyn Axislike>>>,
dual_axislike_map: HashMap<A, Vec<Box<dyn DualAxislike>>>,
associated_gamepad: Option<Gamepad>,
}
impl<A: Actionlike> Default for InputMap<A> {
fn default() -> Self {
InputMap {
buttonlike_map: HashMap::default(),
axislike_map: HashMap::default(),
dual_axislike_map: HashMap::default(),
associated_gamepad: None,
}
}
}
impl<A: Actionlike> InputMap<A> {
#[inline(always)]
pub fn new(bindings: impl IntoIterator<Item = (A, impl Buttonlike)>) -> Self {
bindings
.into_iter()
.fold(Self::default(), |map, (action, input)| {
map.with(action, input)
})
}
#[inline(always)]
pub fn with(mut self, action: A, button: impl Buttonlike) -> Self {
self.insert(action, button);
self
}
#[inline(always)]
pub fn with_axis(mut self, action: A, axis: impl Axislike) -> Self {
self.insert_axis(action, axis);
self
}
#[inline(always)]
pub fn with_dual_axis(mut self, action: A, axis: impl DualAxislike) -> Self {
self.insert_dual_axis(action, axis);
self
}
#[inline(always)]
pub fn with_one_to_many(
mut self,
action: A,
inputs: impl IntoIterator<Item = impl Buttonlike>,
) -> Self {
self.insert_one_to_many(action, inputs);
self
}
#[inline(always)]
pub fn with_multiple(
mut self,
bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
) -> Self {
self.insert_multiple(bindings);
self
}
}
impl<A: Actionlike> InputMap<A> {
#[inline(always)]
fn insert_boxed(&mut self, action: A, button: Box<dyn Buttonlike>) -> &mut Self {
if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
if !bindings.contains(&button) {
bindings.push(button);
}
} else {
self.buttonlike_map.insert(action, vec![button]);
}
self
}
#[inline(always)]
pub fn insert(&mut self, action: A, button: impl Buttonlike) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::Button,
"Cannot map a buttonlike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::Button {
error!(
"Cannot map a buttonlike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
self.insert_boxed(action, Box::new(button));
self
}
#[inline(always)]
pub fn insert_axis(&mut self, action: A, axis: impl Axislike) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::Axis,
"Cannot map a axislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::Axis {
error!(
"Cannot map a axislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
let axis = Box::new(axis) as Box<dyn Axislike>;
if let Some(bindings) = self.axislike_map.get_mut(&action) {
if !bindings.contains(&axis) {
bindings.push(axis);
}
} else {
self.axislike_map.insert(action, vec![axis]);
}
self
}
#[inline(always)]
pub fn insert_dual_axis(&mut self, action: A, dual_axis: impl DualAxislike) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::DualAxis,
"Cannot map a axislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::DualAxis {
error!(
"Cannot map a axislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
let dual_axis = Box::new(dual_axis) as Box<dyn DualAxislike>;
if let Some(bindings) = self.dual_axislike_map.get_mut(&action) {
if !bindings.contains(&dual_axis) {
bindings.push(dual_axis);
}
} else {
self.dual_axislike_map.insert(action, vec![dual_axis]);
}
self
}
#[inline(always)]
pub fn insert_one_to_many(
&mut self,
action: A,
inputs: impl IntoIterator<Item = impl Buttonlike>,
) -> &mut Self {
let inputs = inputs
.into_iter()
.map(|input| Box::new(input) as Box<dyn Buttonlike>);
if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
for input in inputs {
if !bindings.contains(&input) {
bindings.push(input);
}
}
} else {
self.buttonlike_map
.insert(action, inputs.unique().collect());
}
self
}
#[inline(always)]
pub fn insert_multiple(
&mut self,
bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
) -> &mut Self {
for (action, input) in bindings.into_iter() {
self.insert(action, input);
}
self
}
pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
if self.associated_gamepad != other.associated_gamepad {
self.clear_gamepad();
}
for (other_action, other_inputs) in other.buttonlike_map.iter() {
for other_input in other_inputs.iter().cloned() {
self.insert_boxed(other_action.clone(), other_input);
}
}
self
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
#[inline]
pub const fn gamepad(&self) -> Option<Gamepad> {
self.associated_gamepad
}
#[inline]
pub fn with_gamepad(mut self, gamepad: Gamepad) -> Self {
self.set_gamepad(gamepad);
self
}
#[inline]
pub fn set_gamepad(&mut self, gamepad: Gamepad) -> &mut Self {
self.associated_gamepad = Some(gamepad);
self
}
#[inline]
pub fn clear_gamepad(&mut self) -> &mut Self {
self.associated_gamepad = None;
self
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
pub fn pressed(
&self,
action: &A,
input_store: &CentralInputStore,
clash_strategy: ClashStrategy,
) -> bool {
let processed_actions =
self.process_actions(&Gamepads::default(), input_store, clash_strategy);
let Some(updated_value) = processed_actions.get(action) else {
return false;
};
match updated_value {
UpdatedValue::Button(state) => *state,
_ => false,
}
}
#[must_use]
pub fn process_actions(
&self,
gamepads: &Gamepads,
input_store: &CentralInputStore,
clash_strategy: ClashStrategy,
) -> UpdatedActions<A> {
let mut updated_actions = UpdatedActions::default();
let gamepad = self.associated_gamepad.unwrap_or(find_gamepad(gamepads));
for (action, _input_bindings) in self.iter_buttonlike() {
let mut final_state = false;
for binding in _input_bindings {
if binding.pressed(input_store, gamepad) {
final_state = true;
break;
}
}
updated_actions.insert(action.clone(), UpdatedValue::Button(final_state));
}
for (action, _input_bindings) in self.iter_axislike() {
let mut final_value = 0.0;
for binding in _input_bindings {
final_value += binding.value(input_store, gamepad);
}
updated_actions.insert(action.clone(), UpdatedValue::Axis(final_value));
}
for (action, _input_bindings) in self.iter_dual_axislike() {
let mut final_value = Vec2::ZERO;
for binding in _input_bindings {
final_value += binding.axis_pair(input_store, gamepad);
}
updated_actions.insert(action.clone(), UpdatedValue::DualAxis(final_value));
}
self.handle_clashes(&mut updated_actions, input_store, clash_strategy, gamepad);
updated_actions
}
}
#[derive(Debug, Clone, PartialEq, Deref, DerefMut)]
pub struct UpdatedActions<A: Actionlike>(pub HashMap<A, UpdatedValue>);
impl<A: Actionlike> UpdatedActions<A> {
pub fn pressed(&self, action: &A) -> bool {
match self.0.get(action) {
Some(UpdatedValue::Button(state)) => *state,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UpdatedValue {
Button(bool),
Axis(f32),
DualAxis(Vec2),
}
impl<A: Actionlike> Default for UpdatedActions<A> {
fn default() -> Self {
Self(HashMap::default())
}
}
impl<A: Actionlike> InputMap<A> {
pub fn iter_buttonlike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Buttonlike>>)> {
self.buttonlike_map.iter()
}
pub fn iter_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Axislike>>)> {
self.axislike_map.iter()
}
pub fn iter_dual_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn DualAxislike>>)> {
self.dual_axislike_map.iter()
}
pub fn buttonlike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Buttonlike)> {
self.buttonlike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Axislike)> {
self.axislike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn dual_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn DualAxislike)> {
self.dual_axislike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn buttonlike_actions(&self) -> impl Iterator<Item = &A> {
self.buttonlike_map.keys()
}
pub fn axislike_actions(&self) -> impl Iterator<Item = &A> {
self.axislike_map.keys()
}
pub fn dual_axislike_actions(&self) -> impl Iterator<Item = &A> {
self.dual_axislike_map.keys()
}
#[must_use]
pub fn get(&self, action: &A) -> Option<Vec<UserInputWrapper>> {
match action.input_control_kind() {
InputControlKind::Button => {
let buttonlike = self.buttonlike_map.get(action)?;
Some(
buttonlike
.iter()
.map(|input| UserInputWrapper::Button(input.clone()))
.collect(),
)
}
InputControlKind::Axis => {
let buttonlike = self.axislike_map.get(action)?;
Some(
buttonlike
.iter()
.map(|input| UserInputWrapper::Axis(input.clone()))
.collect(),
)
}
InputControlKind::DualAxis => {
let buttonlike = self.dual_axislike_map.get(action)?;
Some(
buttonlike
.iter()
.map(|input| UserInputWrapper::DualAxis(input.clone()))
.collect(),
)
}
}
}
#[must_use]
pub fn get_buttonlike(&self, action: &A) -> Option<&Vec<Box<dyn Buttonlike>>> {
self.buttonlike_map.get(action)
}
#[must_use]
pub fn get_buttonlike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Buttonlike>>> {
self.buttonlike_map.get_mut(action)
}
#[must_use]
pub fn get_axislike(&self, action: &A) -> Option<&Vec<Box<dyn Axislike>>> {
self.axislike_map.get(action)
}
#[must_use]
pub fn get_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Axislike>>> {
self.axislike_map.get_mut(action)
}
#[must_use]
pub fn get_dual_axislike(&self, action: &A) -> Option<&Vec<Box<dyn DualAxislike>>> {
self.dual_axislike_map.get(action)
}
#[must_use]
pub fn len(&self) -> usize {
self.buttonlike_map
.values()
.map(|inputs| inputs.len())
.sum()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
self.buttonlike_map.clear();
self.axislike_map.clear();
self.dual_axislike_map.clear();
}
}
impl<A: Actionlike> InputMap<A> {
pub fn clear_action(&mut self, action: &A) {
match action.input_control_kind() {
InputControlKind::Button => {
self.buttonlike_map.remove(action);
}
InputControlKind::Axis => {
self.axislike_map.remove(action);
}
InputControlKind::DualAxis => {
self.dual_axislike_map.remove(action);
}
}
}
pub fn remove_at(&mut self, action: &A, index: usize) -> Option<()> {
match action.input_control_kind() {
InputControlKind::Button => {
let input_bindings = self.buttonlike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
InputControlKind::Axis => {
let input_bindings = self.axislike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
InputControlKind::DualAxis => {
let input_bindings = self.dual_axislike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
}
}
pub fn remove(&mut self, action: &A, input: impl Buttonlike) -> Option<usize> {
let bindings = self.buttonlike_map.get_mut(action)?;
let boxed_input: Box<dyn Buttonlike> = Box::new(input);
let index = bindings.iter().position(|input| input == &boxed_input)?;
bindings.remove(index);
Some(index)
}
}
impl<A: Actionlike, U: Buttonlike> From<HashMap<A, Vec<U>>> for InputMap<A> {
fn from(raw_map: HashMap<A, Vec<U>>) -> Self {
let mut input_map = Self::default();
for (action, inputs) in raw_map.into_iter() {
input_map.insert_one_to_many(action, inputs);
}
input_map
}
}
impl<A: Actionlike, U: Buttonlike> FromIterator<(A, U)> for InputMap<A> {
fn from_iter<T: IntoIterator<Item = (A, U)>>(iter: T) -> Self {
let mut input_map = Self::default();
for (action, input) in iter.into_iter() {
input_map.insert(action, input);
}
input_map
}
}
#[cfg(feature = "keyboard")]
mod tests {
use bevy::prelude::Reflect;
use serde::{Deserialize, Serialize};
use super::*;
use crate as leafwing_input_manager;
use crate::prelude::*;
#[derive(
Actionlike,
Serialize,
Deserialize,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
Reflect,
)]
enum Action {
Run,
Jump,
Hide,
}
#[test]
fn creation() {
use bevy::input::keyboard::KeyCode;
let input_map = InputMap::default()
.with(Action::Run, KeyCode::KeyW)
.with(Action::Run, KeyCode::ShiftLeft)
.with(Action::Run, KeyCode::ShiftLeft)
.with_one_to_many(Action::Run, [KeyCode::KeyR, KeyCode::ShiftRight])
.with_multiple([
(Action::Jump, KeyCode::Space),
(Action::Hide, KeyCode::ControlLeft),
(Action::Hide, KeyCode::ControlRight),
]);
let expected_bindings: HashMap<Box<dyn Buttonlike>, Action> = HashMap::from([
(Box::new(KeyCode::KeyW) as Box<dyn Buttonlike>, Action::Run),
(
Box::new(KeyCode::ShiftLeft) as Box<dyn Buttonlike>,
Action::Run,
),
(Box::new(KeyCode::KeyR) as Box<dyn Buttonlike>, Action::Run),
(
Box::new(KeyCode::ShiftRight) as Box<dyn Buttonlike>,
Action::Run,
),
(
Box::new(KeyCode::Space) as Box<dyn Buttonlike>,
Action::Jump,
),
(
Box::new(KeyCode::ControlLeft) as Box<dyn Buttonlike>,
Action::Hide,
),
(
Box::new(KeyCode::ControlRight) as Box<dyn Buttonlike>,
Action::Hide,
),
]);
for (action, input) in input_map.buttonlike_bindings() {
let expected_action = expected_bindings.get(input).unwrap();
assert_eq!(expected_action, action);
}
}
#[test]
fn insertion_idempotency() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::Space);
let expected: Vec<Box<dyn Buttonlike>> = vec![Box::new(KeyCode::Space)];
assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
input_map.insert(Action::Run, KeyCode::Space);
assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
}
#[test]
fn multiple_insertion() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::Space);
input_map.insert(Action::Run, KeyCode::Enter);
let expected: Vec<Box<dyn Buttonlike>> =
vec![Box::new(KeyCode::Space), Box::new(KeyCode::Enter)];
assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
}
#[test]
fn input_clearing() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::Space);
input_map.clear_action(&Action::Run);
assert_eq!(input_map, InputMap::default());
input_map.insert(Action::Run, KeyCode::Space);
input_map.insert(Action::Run, KeyCode::ShiftLeft);
assert!(input_map.remove_at(&Action::Run, 1).is_some());
assert!(
input_map.remove_at(&Action::Run, 1).is_none(),
"Should return None on second removal at the same index"
);
assert!(input_map.remove_at(&Action::Run, 0).is_some());
assert!(
input_map.remove_at(&Action::Run, 0).is_none(),
"Should return None on second removal at the same index"
);
}
#[test]
fn merging() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
let mut default_keyboard_map = InputMap::default();
default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft);
default_keyboard_map.insert(
Action::Hide,
ButtonlikeChord::new([KeyCode::ControlLeft, KeyCode::KeyH]),
);
let mut default_gamepad_map = InputMap::default();
default_gamepad_map.insert(Action::Run, KeyCode::Numpad0);
default_gamepad_map.insert(Action::Hide, KeyCode::Numpad7);
input_map.merge(&default_keyboard_map);
assert_eq!(input_map, default_keyboard_map);
input_map.merge(&default_keyboard_map);
assert_eq!(input_map, default_keyboard_map);
}
#[cfg(feature = "gamepad")]
#[test]
fn gamepad_swapping() {
use bevy::input::gamepad::Gamepad;
let mut input_map = InputMap::<Action>::default();
assert_eq!(input_map.gamepad(), None);
input_map.set_gamepad(Gamepad { id: 0 });
assert_eq!(input_map.gamepad(), Some(Gamepad { id: 0 }));
input_map.clear_gamepad();
assert_eq!(input_map.gamepad(), None);
}
}