From 679d6ebe4f2ae52f2fb1c97dc6b10031385196cd Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Tue, 2 Aug 2022 15:05:11 +0200 Subject: [PATCH] Working on inputs --- Cargo.toml | 1 + src/engine/input.rs | 110 +++++++++++++++++++++++++++++++++++++++++++- src/engine/mod.rs | 11 +++-- 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b41a61a..42793be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,4 +25,5 @@ features = [ 'Performance', 'Gamepad', 'GamepadEvent', + 'GamepadButton', ] diff --git a/src/engine/input.rs b/src/engine/input.rs index 1163092..d4eeadf 100644 --- a/src/engine/input.rs +++ b/src/engine/input.rs @@ -20,7 +20,7 @@ pub enum Event { } /// The different actions that a user can do. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum Button { /// The user asks to go to the left. Left, @@ -64,11 +64,117 @@ impl InputManager { events: VecDeque::new(), } } + + /// Adds a gamepad to the input manager. + pub fn add_gamepad(&mut self, gamepad: web_sys::Gamepad) { + self.gamepads.push(Gamepad::new(gamepad)); + } + + /// Updates the gamepad states. + pub fn update(&mut self) { + for gamepad in &mut self.gamepads { + gamepad.update(); + } + } } /// Holds the gamepad information. #[derive(Clone)] pub struct Gamepad { - /// The inner gamepad. + /// The javascript gamepad object. inner: web_sys::Gamepad, + + /// What buttons are pressed or released. + state: HashMap, +} + +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) -> VecDeque { + let mut events = VecDeque::new(); + + 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)); + } + } + + events + } + + /// 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::::into(buttons.get(0)).pressed(), + Button::Button1 => Into::::into(buttons.get(0)).pressed(), + Button::Button2 => Into::::into(buttons.get(0)).pressed(), + Button::Button3 => Into::::into(buttons.get(0)).pressed(), + } + } +} + +/// A helper to easily deal with inputs. +#[derive(Clone)] +pub struct Inputs(Rc>); + +impl Inputs { + /// Creates a new inputs object. + pub fn new(window: &web_sys::Window) -> Result { + let inputs = Inputs(Rc::new(RefCell::new(InputManager::new()))); + + let clone = inputs.clone(); + let cb = Closure::::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())?; + + 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(); + } } diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 33538de..1b135e4 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -21,7 +21,7 @@ use wasm_bindgen::JsCast; use crate::engine::bbox::Bbox; use crate::engine::character::Character; use crate::engine::controls::Controls; -use crate::engine::input::InputManager; +use crate::engine::input::Inputs; use crate::engine::map::Map; use crate::engine::math::now; use crate::engine::scene::{Scene, State}; @@ -118,7 +118,7 @@ pub struct Engine { pub textures: TextureManager, /// The input manager. - pub inputs: InputManager, + pub inputs: Inputs, /// The web page window. pub window: web_sys::Window, @@ -160,7 +160,7 @@ impl Engine { scene, after_loop: now(&performance), textures: TextureManager::new()?, - inputs: InputManager::new(), + inputs: Inputs::new(&window)?, window, document, context, @@ -170,11 +170,14 @@ impl Engine { /// Triggers the update of the model of the engine. pub fn update(&mut self) -> Result<()> { - // Manage the physics + // Manage time correctly let now = now(&self.performance); let duration = unwrap!(now.duration_since(self.after_loop).ok()); self.after_loop = now; + // Update inputs that need to be updated + self.inputs.update(); + if self.document.has_focus()? { // Manage events // while let Some(event) = inner.keyboard.pop() {