From 7d500aa9c0c7db0ff0e8240057d9bb585e2b220a Mon Sep 17 00:00:00 2001 From: Shute Date: Tue, 10 Sep 2024 21:33:34 +0800 Subject: [PATCH] Fix `InputMap::merge` (#621) * Fix `InputMap::merge` * Remove unnecessary swizzle * minor * minor * RELEASES.md --- RELEASES.md | 3 +- examples/twin_stick_controller.rs | 5 +- src/input_map.rs | 84 +++++++++++++++---------------- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index c01bce8f..5712cf53 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,7 +9,7 @@ - added `TripleAxislikeChord` that groups a `Buttonlike` and a `TripleAxislike` together. - added related variants such as: - `InputControlType::TripleAxis` - - `ActionDiff::TripleAxisChanged` + - `ActionDiff::TripleAxisChanged` ### Usability (0.15.1) @@ -32,6 +32,7 @@ ### Bugs (0.15.1) - `InputMap::get_pressed` and siblings now check if the action kind is buttonlike before checking if they are pressed or released, avoiding a debug-mode panic +- `InputMap::merge` is now compatible with all input kinds, previously limited to buttons ## Version 0.15.0 diff --git a/examples/twin_stick_controller.rs b/examples/twin_stick_controller.rs index 9d13b7b7..743f7410 100644 --- a/examples/twin_stick_controller.rs +++ b/examples/twin_stick_controller.rs @@ -160,14 +160,13 @@ fn control_player( if action_state.axis_pair(&PlayerAction::Move) != Vec2::ZERO { // Note: In a real game we'd feed this into an actual player controller // and respects the camera extrinsics to ensure the direction is correct - let move_delta = - time.delta_seconds() * action_state.clamped_axis_pair(&PlayerAction::Move).xy(); + let move_delta = time.delta_seconds() * action_state.clamped_axis_pair(&PlayerAction::Move); player_transform.translation += Vec3::new(move_delta.x, 0.0, move_delta.y); println!("Player moved to: {}", player_transform.translation.xz()); } if action_state.axis_pair(&PlayerAction::Look) != Vec2::ZERO { - let look = action_state.axis_pair(&PlayerAction::Look).xy().normalize(); + let look = action_state.axis_pair(&PlayerAction::Look).normalize(); println!("Player looking in direction: {}", look); } diff --git a/src/input_map.rs b/src/input_map.rs index 0b037d50..d9632fb7 100644 --- a/src/input_map.rs +++ b/src/input_map.rs @@ -1,6 +1,7 @@ //! This module contains [`InputMap`] and its supporting methods and impls. use std::fmt::Debug; +use std::hash::Hash; #[cfg(feature = "asset")] use bevy::asset::Asset; @@ -222,26 +223,23 @@ impl InputMap { } } -// Insertion -impl InputMap { - /// Inserts a binding between an `action` and a specific boxed dyn [`Buttonlike`]. - /// Multiple inputs can be bound to the same action. - /// - /// This method ensures idempotence, meaning that adding the same input - /// for the same action multiple times will only result in a single binding being created. - #[inline(always)] - fn insert_boxed(&mut self, action: A, button: Box) -> &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]); +#[inline(always)] +fn insert_unique(map: &mut HashMap>, key: &K, value: V) +where + K: Clone + Eq + Hash, + V: PartialEq, +{ + if let Some(list) = map.get_mut(key) { + if !list.contains(&value) { + list.push(value); } - - self + } else { + map.insert(key.clone(), vec![value]); } +} +// Insertion +impl InputMap { /// Inserts a binding between an `action` and a specific [`Buttonlike`] `input`. /// Multiple inputs can be bound to the same action. /// @@ -267,7 +265,7 @@ impl InputMap { return self; } - self.insert_boxed(action, Box::new(button)); + insert_unique(&mut self.buttonlike_map, &action, Box::new(button)); self } @@ -296,14 +294,7 @@ impl InputMap { return self; } - let axis = Box::new(axis) as Box; - 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]); - } + insert_unique(&mut self.axislike_map, &action, Box::new(axis)); self } @@ -332,14 +323,7 @@ impl InputMap { return self; } - let dual_axis = Box::new(dual_axis) as Box; - 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]); - } + insert_unique(&mut self.dual_axislike_map, &action, Box::new(dual_axis)); self } @@ -368,14 +352,8 @@ impl InputMap { return self; } - let triple_axis = Box::new(triple_axis) as Box; - if let Some(bindings) = self.triple_axislike_map.get_mut(&action) { - if !bindings.contains(&triple_axis) { - bindings.push(triple_axis); - } - } else { - self.triple_axislike_map.insert(action, vec![triple_axis]); - } + let boxed = Box::new(triple_axis); + insert_unique(&mut self.triple_axislike_map, &action, boxed); self } @@ -433,9 +411,27 @@ impl InputMap { self.clear_gamepad(); } - for (other_action, other_inputs) in other.buttonlike_map.iter() { + for (other_action, other_inputs) in other.iter_buttonlike() { + for other_input in other_inputs.iter().cloned() { + insert_unique(&mut self.buttonlike_map, other_action, other_input); + } + } + + for (other_action, other_inputs) in other.iter_axislike() { + for other_input in other_inputs.iter().cloned() { + insert_unique(&mut self.axislike_map, other_action, other_input); + } + } + + for (other_action, other_inputs) in other.iter_dual_axislike() { + for other_input in other_inputs.iter().cloned() { + insert_unique(&mut self.dual_axislike_map, other_action, other_input); + } + } + + for (other_action, other_inputs) in other.iter_triple_axislike() { for other_input in other_inputs.iter().cloned() { - self.insert_boxed(other_action.clone(), other_input); + insert_unique(&mut self.triple_axislike_map, other_action, other_input); } }