diff --git a/Cargo.toml b/Cargo.toml index 5ad4dfd..b41a61a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,6 @@ features = [ 'HtmlImageElement', 'Window', 'Performance', + 'Gamepad', + 'GamepadEvent', ] diff --git a/src/engine/character.rs b/src/engine/character.rs index 149db05..e0692e4 100644 --- a/src/engine/character.rs +++ b/src/engine/character.rs @@ -135,6 +135,11 @@ impl Character { &self.controls } + /// Changes the controls. + pub fn set_controls(&mut self, controls: Option) { + self.controls = controls; + } + /// Returns a view that looks at the character. pub fn view(&self) -> Bbox { Bbox::from_center_and_size(self.position, Vector::new(24.0 * 16.0, 24.0 * 9.0)) diff --git a/src/engine/controls.rs b/src/engine/controls.rs index 3770878..797c7b7 100644 --- a/src/engine/controls.rs +++ b/src/engine/controls.rs @@ -16,6 +16,9 @@ pub enum Action { pub enum Controls { /// A keyboard controller. Keyboard(KeyboardMap), + + /// A gamepad controller. + Gamepad(GamepadMap), } impl Controls { @@ -24,15 +27,12 @@ impl Controls { Controls::Keyboard(KeyboardMap::default()) } - // /// Returns the default gamepad controls from id. - // /// - // /// Returns None if the gamepad corresponding to the id is not connected. - // pub fn default_gamepad_from_id(id: u32) -> Option { - // match GamepadMap::from_id(id) { - // Some(map) => Some(Controls::Gamepad(map)), - // None => None, - // } - // } + /// Returns the default gamepad controls from id. + /// + /// Returns None if the gamepad corresponding to the id is not connected. + pub fn default_gamepad(gamepad: web_sys::Gamepad) -> Controls { + Controls::Gamepad(GamepadMap::from_gamepad(gamepad)) + } // /// Returns the default gamepad controls from id. // /// @@ -48,7 +48,7 @@ impl Controls { pub fn convert(&self, event: &Event) -> Option { match self { Controls::Keyboard(ref map) => map.convert(event), - // Controls::Gamepad(ref map) => map.convert(event), + Controls::Gamepad(ref map) => map.convert(event), } } @@ -56,7 +56,7 @@ impl Controls { pub fn direction(&self, keyboard: &Keyboard) -> Vector { match self { Controls::Keyboard(ref map) => map.direction(keyboard), - // Controls::Gamepad(ref map) => map.direction(), + Controls::Gamepad(ref map) => map.direction(), } } @@ -120,3 +120,60 @@ impl KeyboardMap { ret } } + +/// A map between gamepad buttons and actions. +#[derive(Clone)] +pub struct GamepadMap { + /// Id of the gamepad. + inner: web_sys::Gamepad, + + /// Number of the jump button. + jump_button: u32, + + /// Number of the run button. + run_button: u32, + // /// Left / Right axis. + // left_right_axis: Axis, +} + +impl GamepadMap { + /// Creates a gamepad map from a javascript gamepad. + pub fn from_gamepad(gamepad: web_sys::Gamepad) -> GamepadMap { + GamepadMap { + inner: gamepad, + jump_button: 0, + run_button: 3, + } + } + + /// Converts an event and depending on the config, returns the corresponding action. + pub fn convert(&self, event: &Event) -> Option { + None + // match event { + // Event::JoystickButtonPressed { joystickid, button } + // if *joystickid == self.id && *button == self.jump_button => + // { + // Some(Action::Jump(true)) + // } + + // Event::JoystickButtonReleased { joystickid, button } + // if *joystickid == self.id && *button == self.jump_button => + // { + // Some(Action::Jump(false)) + // } + + // _ => None, + // } + } + + /// Returns the direction of the directionnal buttons of the gamepad. + pub fn direction(&self) -> Vector { + Vector::new(0.0, 0.0) + // Vector2::new(axis_position(self.id, self.left_right_axis) / 100.0, 0.0) + } + + // /// Returns whether the run button is held down. + // pub fn is_running(&self) -> bool { + // is_button_pressed(self.id, self.run_button) + // } +} diff --git a/src/engine/mod.rs b/src/engine/mod.rs index d542906..6d87ff8 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -20,7 +20,7 @@ use wasm_bindgen::JsCast; use crate::engine::bbox::Bbox; use crate::engine::character::Character; -use crate::engine::controls::Controls; +use crate::engine::controls::{Controls, GamepadMap}; use crate::engine::event::{Key, Keyboard}; use crate::engine::map::Map; use crate::engine::math::now; @@ -84,13 +84,24 @@ impl Engine { let inner = InnerEngine::new(&document, &performance, scene)?; + let inner = Rc::new(RefCell::new(inner)); let window = Rc::new(window); let document = Rc::new(document); let performance = Rc::new(performance); let context = Rc::new(context); + let clone = inner.clone(); + let cb = Closure::::new(move |event: web_sys::GamepadEvent| { + let mut inner = clone.borrow_mut(); + inner.add_gamepad(&event); + }); + + (*window).add_event_listener_with_callback("gamepadconnected", cb.as_ref().unchecked_ref()); + + cb.forget(); + Ok(Engine { - inner: Rc::new(RefCell::new(inner)), + inner, window, document, performance, @@ -146,7 +157,7 @@ impl Engine { let duration = unwrap!(now.duration_since(inner.after_loop).ok()); inner.after_loop = now; - if (self.document.has_focus()?) { + if self.document.has_focus()? { // Manage events while let Some(event) = inner.keyboard.pop() { inner.scene.manage_event(&event); @@ -231,6 +242,9 @@ pub struct InnerEngine { /// The keyboard. pub keyboard: Keyboard, + /// The gamepads. + pub gamepads: Vec, + /// The texture manager. pub textures: TextureManager, } @@ -246,9 +260,20 @@ impl InnerEngine { scene, after_loop: now(&performance), keyboard: Keyboard::new(document)?, + gamepads: vec![], textures: TextureManager::new()?, }) } + + /// Adds a gamepad. + pub fn add_gamepad(&mut self, gamepad: &web_sys::GamepadEvent) { + let gamepad = GamepadMap::from_gamepad(gamepad.gamepad().unwrap()); + let clone = gamepad.clone(); + self.gamepads.push(clone); + + let controlable = self.scene.controlable_mut().unwrap(); + controlable.set_controls(Some(Controls::Gamepad(gamepad))); + } } /// Our custom drawable trait. diff --git a/src/engine/scene.rs b/src/engine/scene.rs index d00ddc6..c8ddabf 100644 --- a/src/engine/scene.rs +++ b/src/engine/scene.rs @@ -55,6 +55,16 @@ impl Scene { None } + /// Returns the controlable. + pub fn controlable_mut(&mut self) -> Option<&mut Character> { + for character in &mut self.characters { + if character.controls().is_some() { + return Some(character); + } + } + None + } + /// Returns the right view. pub fn view(&self) -> Option { let view = self.controlable()?.view(); @@ -109,7 +119,7 @@ impl Scene { c.ground_collision(); } - if (damage) { + if damage { c.die(); state = State::Finished; }