186 lines
5.0 KiB
Rust
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()
|
|
}
|
|
}
|