From 1242a42f0e5c830219df59aca677cff0f6109a50 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Sat, 13 Oct 2018 18:30:44 +0200 Subject: [PATCH] Added support for controllers --- src/app/game.rs | 13 ++- src/engine/character/mod.rs | 40 ++++--- src/engine/controls/mod.rs | 226 +++++++++++++++++++++++++++++++++++- 3 files changed, 256 insertions(+), 23 deletions(-) diff --git a/src/app/game.rs b/src/app/game.rs index fa5363a..857ff8e 100644 --- a/src/app/game.rs +++ b/src/app/game.rs @@ -13,8 +13,10 @@ use clap::{ use sfml::window::{ Event, Key, + joystick, }; + use rusty::engine::scene::Scene; use rusty::engine::character::Character; use rusty::engine::controls::Controls; @@ -60,7 +62,16 @@ fn main() { let resolution = parse_resolution(matches.value_of("resolution").unwrap()).unwrap(); let fullscreen = matches.is_present("fullscreen"); - let mut character = Character::with_controls(Controls::new()); + joystick::update(); + + let gamepads = Controls::all_gamepads(); + let controls = if gamepads.is_empty() { + Controls::default_keyboard() + } else { + gamepads[0].clone() + }; + + let mut character = Character::with_controls(controls); character.set_position((10.0, 0.0)); let mut scene = Scene::new(); diff --git a/src/engine/character/mod.rs b/src/engine/character/mod.rs index 3de743c..39a4f1d 100644 --- a/src/engine/character/mod.rs +++ b/src/engine/character/mod.rs @@ -5,7 +5,6 @@ use std::time::{ use sfml::system::Vector2; use sfml::window::Event; -use sfml::window::Key; use sfml::graphics::{ IntRect, FloatRect, @@ -13,7 +12,11 @@ use sfml::graphics::{ }; use engine::scene::Updatable; -use engine::controls::Controls; +use engine::controls::{ + Controls, + Action, +}; + use engine::renderer::Drawable; use engine::texture::Texture; use engine::physics; @@ -158,13 +161,12 @@ impl Updatable for Character { let mut force: Vector2 = Vector2::new(0.0, 0.0); - // Manage the input of the player - if Key::Left.is_pressed() { - force.x -= 1.0; - } + match self.controls { + Some(ref controls) => { + force += controls.direction(); + } - if Key::Right.is_pressed() { - force.x += 1.0; + _ => (), } if let Some(side) = Side::from_force(force) { @@ -181,7 +183,6 @@ impl Updatable for Character { } - let duration = duration_as_f32(duration); // Compute acceleration @@ -201,22 +202,25 @@ impl Updatable for Character { } fn manage_event(&mut self, event: &Event) { - match event { + let action = if let Some(ref controls) = self.controls { + controls.convert(event) + } else { + None + }; - Event::KeyPressed { - code: Key::Return, .. - } => { + match action { + Some(Action::Jump(true)) => { self.jump(); self.can_jump = false; - } - , + }, - Event::KeyReleased { - code: Key::Return, .. - } => self.can_jump = true, + Some(Action::Jump(false)) => { + self.can_jump = true; + } _ => (), } + } } diff --git a/src/engine/controls/mod.rs b/src/engine/controls/mod.rs index cbe1df0..42269a8 100644 --- a/src/engine/controls/mod.rs +++ b/src/engine/controls/mod.rs @@ -1,14 +1,232 @@ +use sfml::system::Vector2; +use sfml::window::joystick::{ + Axis, + axis_position, + is_connected, + COUNT, +}; + +use sfml::window::{ + Key, + Event, +}; + +/// The different actions that a user can do. +pub enum Action { + /// The jump button. + /// + /// A bool at true means that the button was pressed, + /// A bool at false means that the button was released. + Jump(bool), +} /// Contains the data needed to manage the controls of the player. -pub struct Controls { +#[derive(Copy, Clone)] +pub enum Controls { + /// A keyboard controller. + Keyboard(KeyboardMap), + /// A gamepad controller. + Gamepad(GamepadMap), } impl Controls { - /// Creates the default controls. - pub fn new() -> Controls { - Controls { + /// Returns the default keyboard controls. + pub fn default_keyboard() -> 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 all_gamepads() -> Vec { + GamepadMap::all() + .into_iter() + .map(|x| Controls::Gamepad(x)) + .collect() + } + + /// Converts an event and depending on the config, returns the corresponding action. + pub fn convert(&self, event: &Event) -> Option { + match self { + Controls::Keyboard(ref map) => map.convert(event), + Controls::Gamepad(ref map) => map.convert(event), + } + } + + /// Returns the direction of the controls. + pub fn direction(&self) -> Vector2 { + match self { + Controls::Keyboard(ref map) => map.direction(), + Controls::Gamepad(ref map) => map.direction(), + } + } + +} + +/// A map between keyboard keys and actions. +#[derive(Copy, Clone)] +pub struct KeyboardMap { + + /// The key corresponding to the jump button. + jump_key: Key, + + /// The key corresponding to the up button. + up_key: Key, + + /// The key corresponding to the left button. + left_key: Key, + + /// The key corresponding to the right button. + right_key: Key, + + /// The key corresponding to the down button. + down_key: Key, + +} + +impl KeyboardMap { + + /// Creates the default keyboard config. + pub fn default() -> KeyboardMap { + KeyboardMap { + jump_key: Key::Space, + up_key: Key::Up, + left_key: Key::Left, + right_key: Key::Right, + down_key: Key::Down, + } + } + + /// Converts an event and depending on the config, returns the corresponding action. + pub fn convert(&self, event: &Event) -> Option { + + match event { + Event::KeyPressed { code, .. } if *code == self.jump_key => + Some(Action::Jump(true)), + + Event::KeyReleased { code, .. } if *code == self.jump_key => + Some(Action::Jump(false)), + + _ => None, + } + + } + + /// Returns the direction of the keys. + pub fn direction(&self) -> Vector2 { + + let mut ret = Vector2::new(0.0, 0.0); + + const RIGHT: Vector2 = Vector2 { x: 1.0, y: 0.0 }; + const UP: Vector2 = Vector2 {x: 0.0, y: 1.0}; + + if self.right_key.is_pressed() { + ret += RIGHT; + } + + if self.left_key.is_pressed() { + ret -= RIGHT; + } + + if self.up_key.is_pressed() { + ret += UP; + } + + if self.down_key.is_pressed() { + ret -= UP; + } + + ret + + } + +} + +/// A map between gamepad buttons and actions. +#[derive(Copy, Clone)] +pub struct GamepadMap { + + /// Id of the gamepad. + id: u32, + + /// Number of the jump button. + jump_button: u32, + + /// Left / Right axis. + left_right_axis: Axis, + +} + +impl GamepadMap { + + /// Creates the default gamepad from an id. + /// + /// Returns None if the gamepad corresponding to the id is not connected. + pub fn from_id(id: u32) -> Option { + + if ! is_connected(id){ + return None; + } + + Some(GamepadMap { + id: id, + jump_button: 1, + left_right_axis: Axis::X, + }) + } + + /// Creates a vector containing all connected gamepads. + pub fn all() -> Vec { + let mut gamepads = vec![]; + + for id in 0 .. COUNT { + if let Some(gamepad) = GamepadMap::from_id(id) { + gamepads.push(gamepad); + } + } + + gamepads + } + + /// Converts an event and depending on the config, returns the corresponding action. + pub fn convert(&self, event: &Event) -> Option { + + 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) -> Vector2 { + Vector2::new( + axis_position(self.id, self.left_right_axis) / 100.0, + 0.0 + ) + } }