game/src/engine/input.rs

306 lines
8.7 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::engine::vector::Vector;
use crate::{log, Result};
/// The different events that can be triggered.
#[derive(Debug, Copy, Clone)]
pub enum Event {
/// A key has been pressed.
KeyPressed(Key),
/// A key has been released.
KeyReleased(Key),
/// A gamepad has been connected.
GamepadConnected(u32),
/// A button has been pressed on a certain gamepad.
ButtonPressed(u32, Button),
/// A button has been released on a certain gamepad.
ButtonReleased(u32, Button),
}
/// The different keyboard keys we support.
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)]
pub enum Key {
/// The left arrow key.
ArrowLeft,
/// The right arrow key.
ArrowRight,
/// The up arrow key.
ArrowUp,
/// The down arrow key.
ArrowDown,
/// The space key.
Space,
}
/// The different actions a user can do.
#[derive(Debug, Copy, Clone)]
pub enum Action {
/// A certain button has been pressed.
ButtonPressed(Button),
/// A certain 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 {
/// The state of the keyboard.
keyboard: HashMap<Key, bool>,
/// 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 {
keyboard: HashMap::new(),
gamepads: vec![],
events: VecDeque::new(),
}
}
/// Gets the gamepad from its id.
pub fn gamepad(&self, id: u32) -> Option<&Gamepad> {
self.gamepads.iter().find(|x| x.inner.index() == id)
}
/// Parses a keyboard event to the key.
pub fn key(event: web_sys::KeyboardEvent) -> Option<Key> {
match event.code().as_str() {
"ArrowDown" => Some(Key::ArrowDown),
"ArrowUp" => Some(Key::ArrowUp),
"ArrowLeft" => Some(Key::ArrowLeft),
"ArrowRight" => Some(Key::ArrowRight),
"Space" => Some(Key::Space),
_ => None,
}
}
/// Adds a key pressed event to the list of events.
pub fn key_pressed_event(&mut self, event: web_sys::KeyboardEvent) {
if let Some(key) = InputManager::key(event) {
self.keyboard.insert(key, true);
self.events.push_back(Event::KeyPressed(key));
}
}
/// Adds a key released event to the list of events.
pub fn key_released_event(&mut self, event: web_sys::KeyboardEvent) {
if let Some(key) = InputManager::key(event) {
self.keyboard.insert(key, false);
self.events.push_back(Event::KeyReleased(key));
}
}
/// Adds a gamepad to the input manager.
pub fn add_gamepad(&mut self, gamepad: web_sys::Gamepad) {
log!("Gamepad added {}", gamepad.id());
self.events
.push_back(Event::GamepadConnected(gamepad.index()));
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);
}
}
/// Checks if a specific key is pressed.
pub fn is_key_pressed(&self, key: Key) -> bool {
*self.keyboard.get(&key).unwrap_or(&false)
}
}
/// Holds the gamepad information.
#[derive(Clone)]
pub struct Gamepad {
/// The javascript gamepad object.
inner: web_sys::Gamepad,
/// The main joystick.
main_joystick: Joystick,
/// What buttons are pressed or released.
buttons: HashMap<Button, bool>,
}
/// The joysticks.
#[derive(Clone)]
pub struct Joystick {
/// The id of the X axis of the joystick.
pub x_id: u32,
/// The id of the Y axis of the joystick.
pub y_id: u32,
}
impl Gamepad {
/// Creates a new gamepad from its javascript gamepad.
pub fn new(inner: web_sys::Gamepad) -> Gamepad {
Gamepad {
inner,
main_joystick: Joystick { x_id: 0, y_id: 1 },
buttons: HashMap::new(),
}
}
/// Returns the direction of the main joystick.
pub fn main_direction(&self) -> Vector {
let axes = self.inner.axes();
let x = axes.get(self.main_joystick.x_id).as_f64().unwrap();
let y = axes.get(self.main_joystick.y_id).as_f64().unwrap();
Vector::new(x, y)
}
/// 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.buttons.insert(button, is_pressed).unwrap_or(false);
if was_pressed && !is_pressed {
// Button was released
events.push_back(Event::ButtonReleased(self.inner.index(), button));
}
if is_pressed && !was_pressed {
// Button was pressed
events.push_back(Event::ButtonPressed(self.inner.index(), button));
}
}
}
/// Checks if a button is pressed.
pub fn is_pressed(&self, button: Button) -> bool {
*self.buttons.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(pub 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();
let clone = inputs.clone();
let cb = Closure::<dyn FnMut(_)>::new(move |event: web_sys::KeyboardEvent| {
let mut inner = clone.0.borrow_mut();
inner.key_pressed_event(event);
});
window.add_event_listener_with_callback("keydown", cb.as_ref().unchecked_ref())?;
cb.forget();
let clone = inputs.clone();
let cb = Closure::<dyn FnMut(_)>::new(move |event: web_sys::KeyboardEvent| {
let mut inner = clone.0.borrow_mut();
inner.key_released_event(event);
});
window.add_event_listener_with_callback("keyup", 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()
}
}