Refactor menu input a bit

This commit is contained in:
2026-09-11 11:38:01 +02:00
parent 3dbda96d31
commit cc850debf2
11 changed files with 282 additions and 189 deletions
+87 -46
View File
@@ -1,65 +1,106 @@
use bevy::prelude::*;
use crate::{game_settings::GameSettings, input::{LocalInputAssignments, LocalInputSource}};
use crate::input::{
GamepadBinding, LocalInputSource, MenuInputMap, MenuInputMapAction,
};
#[derive(Default)]
#[derive(Default, Clone)]
pub struct MenuInput {
pub movement: Vec2,
pub accept: bool
pub accept: bool,
pub back: bool,
pub start: bool,
}
#[derive(Resource, Default)]
pub struct MenuInputState {
pub inputs: Vec<(LocalInputSource, MenuInput)>,
}
impl MenuInputState {
pub fn get(&self, source: &LocalInputSource) -> MenuInput {
self.inputs.iter().find(|(candidate, _)| candidate == source).map(|(_, input)| input.clone()).unwrap_or_default()
}
pub fn aggregate(&self) -> MenuInput {
self.inputs.iter().fold(MenuInput::default(), |mut result, (_, input)| {
result.movement += input.movement;
result.accept |= input.accept;
result.back |= input.back;
result.start |= input.start;
result
})
}
}
impl MenuInput {
pub fn accumulate(&mut self, other: &Self) {
self.movement += other.movement;
self.movement = self.movement.normalize();
self.accept = self.accept || other.accept;
self.accept |= other.accept;
self.back |= other.back;
self.start |= other.start;
self.movement = self.movement.clamp_length_max(1.0);
}
}
pub fn from_gamepad(gamepad: &Gamepad, game_settings: &GameSettings) -> Self {
let mut x = gamepad.left_stick().x;
let mut y = gamepad.left_stick().y;
let deadzone = game_settings.input_common.stick_deadzone.to_num::<f32>();
if x.abs() < deadzone {
x = 0.0;
}
if y.abs() < deadzone {
y = 0.0;
}
MenuInput {
movement: Vec2::new(x, y),
accept: gamepad.pressed(GamepadButton::South)
fn keyboard_input(key: &ButtonInput<KeyCode>, map: &MenuInputMap) -> MenuInput {
let mut input = MenuInput::default();
for binding in &map.keyboard {
let pressed = key.pressed(binding.key);
match binding.action {
MenuInputMapAction::MovementXDir(sign) => {
input.movement.x += if pressed { sign as f32 } else { 0.0 };
}
MenuInputMapAction::MovementYDir(sign) => {
input.movement.y += if pressed { sign as f32 } else { 0.0 };
}
MenuInputMapAction::Accept => input.accept |= pressed,
MenuInputMapAction::Back => input.back |= pressed,
MenuInputMapAction::Start => input.start |= pressed,
}
}
pub fn from_keyboard(keeb: &ButtonInput<KeyCode>) -> Self {
let x = if keeb.pressed(KeyCode::KeyA) { -1.0 } else { 0.0 };
let x = x + if keeb.pressed(KeyCode::KeyD) { 1.0 } else { 0.0 };
let y = if keeb.pressed(KeyCode::KeyS) { -1.0 } else { 0.0 };
let y = y + if keeb.pressed(KeyCode::KeyW) { 1.0 } else { 0.0 };
input.accept = map.keyboard.iter().any(|binding| matches!(binding.action, MenuInputMapAction::Accept) && key.pressed(binding.key));
input.back = map.keyboard.iter().any(|binding| matches!(binding.action, MenuInputMapAction::Back) && key.pressed(binding.key));
input.start = map.keyboard.iter().any(|binding| matches!(binding.action, MenuInputMapAction::Start) && key.pressed(binding.key));
input.movement = input.movement.normalize_or_zero();
input
}
let movement = Vec2::new(x, y);
MenuInput {
movement: movement,
accept: keeb.pressed(KeyCode::Enter)
fn gamepad_input(pad: &Gamepad, map: &MenuInputMap, deadzone: f32) -> MenuInput {
let mut input = MenuInput::default();
for binding in &map.gamepad {
let (value, just_pressed) = match binding.binding {
GamepadBinding::Button(button) => (if pad.pressed(button) { 1.0 } else { 0.0 }, pad.just_pressed(button)),
GamepadBinding::Axis(axis, sign) => (pad.get(axis).unwrap_or_default() * sign as f32, false),
};
match binding.action {
MenuInputMapAction::MovementXDir(sign) => {
input.movement.x += value * sign as f32;
}
MenuInputMapAction::MovementYDir(sign) => {
input.movement.y += value * sign as f32;
}
MenuInputMapAction::Accept => { input.accept |= value > 0.5; input.accept |= just_pressed; }
MenuInputMapAction::Back => { input.back |= value > 0.5; input.back |= just_pressed; }
MenuInputMapAction::Start => { input.start |= value > 0.5; input.start |= just_pressed; }
}
}
if input.movement.x.abs() < deadzone { input.movement.x = 0.0; }
if input.movement.y.abs() < deadzone { input.movement.y = 0.0; }
input.movement = input.movement.clamp_length_max(1.0);
input
}
pub fn from_source(source: &LocalInputSource, key: &ButtonInput<KeyCode>, gamepads: &Query<(Entity, &Gamepad)>, assignments: &LocalInputAssignments, game_settings: &GameSettings) -> Self {
match source {
LocalInputSource::Keyboard => Self::from_keyboard(key),
LocalInputSource::Gamepad(entity) => {
let gamepad = gamepads.get(*entity);
if let Ok((_, gamepad)) = gamepad {
Self::from_gamepad(gamepad, game_settings)
} else {
Self::default()
}
},
}
pub fn sample_menu_inputs(
key: Res<ButtonInput<KeyCode>>,
map: Res<MenuInputMap>,
pads: Query<(Entity, &Gamepad)>,
mut state: ResMut<MenuInputState>,
) {
state.inputs.clear();
let deadzone = 0.275;
state.inputs.push((LocalInputSource::Keyboard, keyboard_input(&key, &map)));
for (entity, pad) in pads.iter() {
state.inputs.push((LocalInputSource::Gamepad(entity), gamepad_input(pad, &map, deadzone)));
}
}
}