game/src/engine/input.rs

186 lines
5.0 KiB
Rust

//! This module helps us deal with controls and events.
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use crate::{log, Result};
/// The different actions that a user can do.
#[derive(Debug, Copy, Clone)]
pub enum Event {
/// A button has been pressed.
ButtonPressed(Button),
/// A button has been released.
ButtonReleased(Button),
}
/// The different actions that a user can do.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Button {
/// The user asks to go to the left.
Left,
/// The user asks to go to the right.
Right,
/// The user asks to go up.
Up,
/// The user asks to go down.
Down,
/// The main action or validatation button.
Button0,
/// The user asks to do the secondary action.
Button1,
/// The user asks to do the tertiary action.
Button2,
/// The user asks to do the 4th action or cancel.
Button3,
}
/// This structure holds what button are pressed or released, and stores the events.
pub struct InputManager {
/// All the connected gamepads.
gamepads: Vec<Gamepad>,
/// The events that have occur.
events: VecDeque<Event>,
}
impl InputManager {
/// Creates a new input manager.
pub fn new() -> InputManager {
InputManager {
gamepads: vec![],
events: VecDeque::new(),
}
}
/// Adds a gamepad to the input manager.
pub fn add_gamepad(&mut self, gamepad: web_sys::Gamepad) {
log!("Gamepad added {}", gamepad.id());
self.gamepads.push(Gamepad::new(gamepad));
}
/// Updates the gamepad states.
pub fn update(&mut self) {
for gamepad in &mut self.gamepads {
gamepad.update(&mut self.events);
}
}
}
/// Holds the gamepad information.
#[derive(Clone)]
pub struct Gamepad {
/// The javascript gamepad object.
inner: web_sys::Gamepad,
/// What buttons are pressed or released.
state: HashMap<Button, bool>,
}
impl Gamepad {
/// Creates a new gamepad from its javascript gamepad.
pub fn new(inner: web_sys::Gamepad) -> Gamepad {
Gamepad {
inner,
state: HashMap::new(),
}
}
/// Updates the state of the gamepad and adds the corresponding events to the deque.
pub fn update(&mut self, events: &mut VecDeque<Event>) {
const BUTTONS: [Button; 4] = [
Button::Button0,
Button::Button1,
Button::Button2,
Button::Button3,
];
for button in BUTTONS {
// Finds the real state of the button
let is_pressed = self.is_js_pressed(button);
// Updates the map and returns the old state of the button
let was_pressed = self.state.insert(button, is_pressed).unwrap_or(false);
if was_pressed && !is_pressed {
// Button was released
events.push_back(Event::ButtonReleased(button));
}
if is_pressed && !was_pressed {
// Button was pressed
events.push_back(Event::ButtonPressed(button));
}
}
}
/// Checks if a button is pressed.
pub fn is_pressed(&self, button: Button) -> bool {
*self.state.get(&button).unwrap_or(&false)
}
/// Utility function to really check the state of the button.
fn is_js_pressed(&self, button: Button) -> bool {
let buttons = self.inner.buttons();
match button {
Button::Left => false,
Button::Right => false,
Button::Up => false,
Button::Down => false,
Button::Button0 => Into::<web_sys::GamepadButton>::into(buttons.get(0)).pressed(),
Button::Button1 => Into::<web_sys::GamepadButton>::into(buttons.get(1)).pressed(),
Button::Button2 => Into::<web_sys::GamepadButton>::into(buttons.get(2)).pressed(),
Button::Button3 => Into::<web_sys::GamepadButton>::into(buttons.get(3)).pressed(),
}
}
}
/// A helper to easily deal with inputs.
#[derive(Clone)]
pub struct Inputs(Rc<RefCell<InputManager>>);
impl Inputs {
/// Creates a new inputs object.
pub fn new(window: &web_sys::Window) -> Result<Inputs> {
let inputs = Inputs(Rc::new(RefCell::new(InputManager::new())));
let clone = inputs.clone();
let cb = Closure::<dyn FnMut(_)>::new(move |event: web_sys::GamepadEvent| {
let mut inner = clone.0.borrow_mut();
inner.add_gamepad(event.gamepad().unwrap());
});
window.add_event_listener_with_callback("gamepadconnected", cb.as_ref().unchecked_ref())?;
cb.forget();
Ok(inputs)
}
/// Updates the inputs.
///
/// This function needs to be called at each frame.
pub fn update(&mut self) {
let mut inner = self.0.borrow_mut();
inner.update();
}
/// Returns the next event.
pub fn next(&mut self) -> Option<Event> {
let mut inner = self.0.borrow_mut();
inner.events.pop_front()
}
}