Detect diagonals better on controllers
This commit is contained in:
+98
-19
@@ -1,4 +1,7 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use gilrs::{Event as GamepadEvent, EventType, GamepadId, ev::Code};
|
||||
use winit::{
|
||||
@@ -8,12 +11,13 @@ use winit::{
|
||||
|
||||
use crate::{
|
||||
emulator::{EmulatorClient, EmulatorCommand, SimId, VBKey},
|
||||
input::{InputMapping, MappingProvider},
|
||||
input::{AxisMapping, InputMapping, MappingProvider},
|
||||
};
|
||||
|
||||
pub struct Controller {
|
||||
pub sim_id: SimId,
|
||||
state: VBKey,
|
||||
axis_values: HashMap<(GamepadId, Code), f32>,
|
||||
mapping: Arc<RwLock<InputMapping>>,
|
||||
}
|
||||
|
||||
@@ -22,6 +26,7 @@ impl Controller {
|
||||
Self {
|
||||
sim_id,
|
||||
state: VBKey::SGN,
|
||||
axis_values: HashMap::new(),
|
||||
mapping: mappings.for_sim(sim_id).clone(),
|
||||
}
|
||||
}
|
||||
@@ -45,22 +50,18 @@ impl Controller {
|
||||
(VBKey::empty(), mappings)
|
||||
}
|
||||
EventType::AxisChanged(_, value, code) => {
|
||||
let (neg, pos) = self.map_axis(&event.id, &code)?;
|
||||
let mut pressed = VBKey::empty();
|
||||
let mut released = VBKey::empty();
|
||||
if value < -0.75 {
|
||||
pressed = pressed.union(neg);
|
||||
}
|
||||
if value > 0.75 {
|
||||
pressed = pressed.union(pos);
|
||||
}
|
||||
if value > -0.65 {
|
||||
released = released.union(neg);
|
||||
}
|
||||
if value < 0.65 {
|
||||
released = released.union(pos);
|
||||
}
|
||||
(pressed, released)
|
||||
let mapping = self.map_axis(&event.id, &code)?;
|
||||
self.axis_values.insert((event.id, code), value);
|
||||
|
||||
let pair_value = mapping
|
||||
.pair
|
||||
.and_then(|p| self.axis_values.get(&(event.id, p)))
|
||||
.copied()
|
||||
.unwrap_or_default();
|
||||
let neg = mapping.neg;
|
||||
let pos = mapping.pos;
|
||||
|
||||
axis_presses(value, pair_value, neg, pos)
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
@@ -87,7 +88,7 @@ impl Controller {
|
||||
self.mapping.read().unwrap().map_button(id, code)
|
||||
}
|
||||
|
||||
fn map_axis(&self, id: &GamepadId, code: &Code) -> Option<(VBKey, VBKey)> {
|
||||
fn map_axis(&self, id: &GamepadId, code: &Code) -> Option<AxisMapping> {
|
||||
self.mapping.read().unwrap().map_axis(id, code)
|
||||
}
|
||||
}
|
||||
@@ -126,3 +127,81 @@ impl ControllerManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn axis_presses(value: f32, pair_value: f32, neg: VBKey, pos: VBKey) -> (VBKey, VBKey) {
|
||||
use std::f32::consts::FRAC_PI_3;
|
||||
|
||||
let mut pressed = VBKey::empty();
|
||||
let mut released = VBKey::empty();
|
||||
|
||||
let magnitude = value.hypot(pair_value);
|
||||
let abs_angle = pair_value.atan2(value);
|
||||
|
||||
if magnitude < 0.65 {
|
||||
released = released.union(neg).union(pos);
|
||||
} else if abs_angle <= FRAC_PI_3 {
|
||||
// stick tilted towards positive
|
||||
released = released.union(neg);
|
||||
|
||||
if magnitude >= 0.75 {
|
||||
pressed = pressed.union(pos);
|
||||
}
|
||||
} else if abs_angle >= 2.0 * FRAC_PI_3 {
|
||||
// stick tilted towards negative
|
||||
released = released.union(pos);
|
||||
|
||||
if magnitude >= 0.75 {
|
||||
pressed = pressed.union(neg);
|
||||
}
|
||||
} else {
|
||||
released = released.union(neg).union(pos);
|
||||
}
|
||||
|
||||
(pressed, released)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{VBKey, axis_presses};
|
||||
|
||||
const NEG: VBKey = VBKey::LL;
|
||||
const POS: VBKey = VBKey::LR;
|
||||
|
||||
const NONE: VBKey = VBKey::empty();
|
||||
const BOTH: VBKey = NEG.union(POS);
|
||||
|
||||
#[test]
|
||||
fn detects_no_input() {
|
||||
let (pressed, released) = axis_presses(0.0, 0.0, NEG, POS);
|
||||
assert_eq!(pressed, NONE);
|
||||
assert_eq!(released, BOTH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_pos_input() {
|
||||
let (pressed, released) = axis_presses(1.0, 0.0, NEG, POS);
|
||||
assert_eq!(pressed, POS);
|
||||
assert_eq!(released, NEG);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_neg_input() {
|
||||
let (pressed, released) = axis_presses(-1.0, 0.0, NEG, POS);
|
||||
assert_eq!(pressed, NEG);
|
||||
assert_eq!(released, POS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn respects_dead_zone() {
|
||||
let (pressed, released) = axis_presses(0.70, 0.0, NEG, POS);
|
||||
assert_eq!(pressed, NONE);
|
||||
assert_eq!(released, NEG);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handles_diagonals_ok() {
|
||||
let (pressed, released) = axis_presses(0.6, 0.6, NEG, POS);
|
||||
assert_eq!(pressed, POS);
|
||||
assert_eq!(released, NEG);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user