1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-07-12 15:56:03 +00:00

Initial support for gamepads (P2 only)

This commit is contained in:
Alula 2020-12-06 15:56:16 +01:00
parent 5f259b572e
commit d0b58a5aa1
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
8 changed files with 251 additions and 25 deletions

View file

@ -55,7 +55,8 @@ directories = "2"
gfx = "0.18"
gfx_core = "0.9"
gfx_device_gl = {git = "https://github.com/doukutsu-rs/gfx.git", branch = "pre-ll"}
ggez = {git = "https://github.com/doukutsu-rs/ggez.git", rev = "aad56b0d173ca9f4aeb28599075b5af49ab9214e"}
ggez = {git = "https://github.com/doukutsu-rs/ggez.git", rev = "d70d3f059077efde7cae02f37e7b077924880128"}
gilrs = "0.8"
glutin = {git = "https://github.com/doukutsu-rs/glutin.git", branch = "android-support"}
imgui = {git = "https://github.com/Gekkio/imgui-rs.git", rev = "a990a538b66cb67dba3a072bf299b6a51c001447"}
imgui-gfx-renderer = {git = "https://github.com/Gekkio/imgui-rs.git", rev = "a990a538b66cb67dba3a072bf299b6a51c001447"}

View file

@ -0,0 +1,195 @@
use ggez::{Context, GameResult};
use gilrs::{Axis, Button, GamepadId};
use num_traits::abs;
use crate::bitfield;
use crate::input::player_controller::PlayerController;
use crate::shared_game_state::SharedGameState;
bitfield! {
#[derive(Clone, Copy)]
pub struct KeyState(u16);
impl Debug;
pub left, set_left: 0;
pub right, set_right: 1;
pub up, set_up: 2;
pub down, set_down: 3;
pub map, set_map: 4;
pub inventory, set_inventory: 5;
pub jump, set_jump: 6;
pub shoot, set_shoot: 7;
pub next_weapon, set_next_weapon: 8;
pub prev_weapon, set_prev_weapon: 9;
pub start, set_start: 10;
}
/// An implementation of player controller backed by gilrs library.
#[derive(Clone)]
pub struct GilrsPlayerController {
gamepad_id: GamepadId,
left_x: f64,
left_y: f64,
right_x: f64,
right_y: f64,
state: KeyState,
old_state: KeyState,
trigger: KeyState,
}
impl GilrsPlayerController {
pub fn new(gamepad_id: GamepadId) -> GilrsPlayerController {
GilrsPlayerController {
gamepad_id,
left_x: 0.0,
left_y: 0.0,
right_x: 0.0,
right_y: 0.0,
state: KeyState(0),
old_state: KeyState(0),
trigger: KeyState(0),
}
}
}
const THRESHOLD: f64 = 0.3;
impl PlayerController for GilrsPlayerController {
fn update(&mut self, state: &SharedGameState, _ctx: &mut Context) -> GameResult {
if let Some(gilrs) = state.gilrs.as_ref() {
if let Some(gamepad) = gilrs.connected_gamepad(self.gamepad_id) {
let mut axes = [
(&mut self.left_x, Axis::LeftStickX),
(&mut self.left_y, Axis::LeftStickY),
(&mut self.right_x, Axis::RightStickX),
(&mut self.right_y, Axis::RightStickY),
];
for (axis_val, id) in axes.iter_mut() {
if let Some(axis) = gamepad.axis_data(*id) {
**axis_val = if abs(axis.value()) < 0.12 { 0.0 } else { axis.value() } as f64;
}
}
self.state.set_up(self.left_y > THRESHOLD);
self.state.set_left(self.left_x < -THRESHOLD);
self.state.set_down(self.left_y < -THRESHOLD);
self.state.set_right(self.left_x > THRESHOLD);
self.state.set_jump(gamepad.is_pressed(Button::South));
self.state.set_shoot(gamepad.is_pressed(Button::East));
self.state.set_prev_weapon(gamepad.is_pressed(Button::LeftTrigger));
self.state.set_next_weapon(gamepad.is_pressed(Button::RightTrigger));
self.state.set_start(gamepad.is_pressed(Button::Start));
}
}
Ok(())
}
fn update_trigger(&mut self) {
let mut trigger = self.state.0 ^ self.old_state.0;
trigger &= self.state.0;
self.old_state = self.state;
self.trigger = KeyState(trigger);
}
fn move_up(&self) -> bool {
self.left_y > THRESHOLD
}
fn move_left(&self) -> bool {
self.left_x < -THRESHOLD
}
fn move_down(&self) -> bool {
self.left_y < -THRESHOLD
}
fn move_right(&self) -> bool {
self.left_x > THRESHOLD
}
fn prev_weapon(&self) -> bool {
self.state.prev_weapon()
}
fn next_weapon(&self) -> bool {
self.state.next_weapon()
}
fn jump(&self) -> bool {
self.state.jump()
}
fn shoot(&self) -> bool {
self.state.shoot()
}
fn trigger_up(&self) -> bool {
self.trigger.up()
}
fn trigger_left(&self) -> bool {
self.trigger.left()
}
fn trigger_down(&self) -> bool {
self.trigger.down()
}
fn trigger_right(&self) -> bool {
self.trigger.right()
}
fn trigger_prev_weapon(&self) -> bool {
self.trigger.prev_weapon()
}
fn trigger_next_weapon(&self) -> bool {
self.trigger.next_weapon()
}
fn trigger_jump(&self) -> bool {
self.trigger.jump()
}
fn trigger_shoot(&self) -> bool {
self.trigger.shoot()
}
fn trigger_menu_ok(&self) -> bool {
self.trigger.jump()
}
fn trigger_menu_back(&self) -> bool {
self.trigger.shoot()
}
fn trigger_menu_pause(&self) -> bool {
self.trigger.start()
}
fn look_up(&self) -> bool {
self.left_y > THRESHOLD || self.right_y > THRESHOLD
}
fn look_left(&self) -> bool {
self.left_x < -THRESHOLD || self.right_x < -THRESHOLD
}
fn look_down(&self) -> bool {
self.left_y < -THRESHOLD || self.right_y < -THRESHOLD
}
fn look_right(&self) -> bool {
self.left_x > THRESHOLD || self.right_x > THRESHOLD
}
fn move_analog_x(&self) -> f64 {
self.left_x
}
fn move_analog_y(&self) -> f64 {
self.left_y
}
}

View file

@ -23,7 +23,7 @@ bitfield! {
pub next_weapon, set_next_weapon: 8;
pub prev_weapon, set_prev_weapon: 9;
pub escape, set_escape: 10;
pub enter, set_enter: 10;
pub enter, set_enter: 11;
}
#[derive(Clone)]

View file

@ -1,5 +1,6 @@
pub mod combined_menu_controller;
pub mod dummy_player_controller;
pub mod gilrs_player_controller;
pub mod keyboard_player_controller;
pub mod player_controller;
pub mod touch_controls;

View file

@ -93,6 +93,12 @@ impl Game {
}
fn update(&mut self, ctx: &mut Context) -> GameResult {
if let Some(gilrs) = self.state.gilrs.as_mut() {
while let Some(_) = gilrs.next_event() {
//
}
}
if let Some(scene) = self.scene.as_mut() {
match self.state.timing_mode {
TimingMode::_50Hz | TimingMode::_60Hz => {
@ -160,10 +166,9 @@ impl Game {
Ok(())
}
fn key_down_event(&mut self, key_code: KeyCode, _key_mod: KeyMods, repeat: bool) {
fn key_down_event(&mut self, key_code: KeyCode, repeat: bool) {
if repeat { return; }
// todo: proper keymaps?
let state = &mut self.state;
match key_code {
KeyCode::F7 => { state.set_speed(1.0) }
@ -183,10 +188,6 @@ impl Game {
_ => {}
}
}
fn key_up_event(&mut self, key_code: KeyCode, _key_mod: KeyMods) {
//
}
}
#[cfg(target_os = "android")]
@ -354,8 +355,6 @@ pub fn init() -> GameResult {
}
WindowEvent::Resized(_) => {
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
let (w, h) = graphics::drawable_size(ctx);
game.state.tmp_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.game_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.lightmap_canvas = Canvas::with_window_size(ctx).unwrap();
@ -373,20 +372,14 @@ pub fn init() -> GameResult {
KeyboardInput {
state: el_state,
virtual_keycode: Some(keycode),
modifiers,
..
},
..
} => {
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
match el_state {
ElementState::Pressed => {
let repeat = keyboard::is_key_repeated(ctx);
game.key_down_event(keycode, modifiers.into(), repeat);
}
ElementState::Released => {
game.key_up_event(keycode, modifiers.into());
}
if el_state == ElementState::Pressed {
let repeat = keyboard::is_key_repeated(ctx);
game.key_down_event(keycode, repeat);
}
}
}

View file

@ -1,10 +1,18 @@
use ggez::{Context, GameResult};
use gilrs::GamepadId;
use serde::{Deserialize, Serialize};
use winit::event::VirtualKeyCode;
use crate::input::keyboard_player_controller::KeyboardController;
use crate::input::player_controller::PlayerController;
use crate::player::TargetPlayer;
use crate::input::gilrs_player_controller::GilrsPlayerController;
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum ControllerType {
Keyboard,
Gamepad(usize),
}
#[derive(Serialize, Deserialize)]
pub struct Settings {
@ -15,6 +23,8 @@ pub struct Settings {
pub touch_controls: bool,
pub player1_key_map: PlayerKeyMap,
pub player2_key_map: PlayerKeyMap,
pub player1_controller_type: ControllerType,
pub player2_controller_type: ControllerType,
#[serde(skip)]
pub speed: f64,
#[serde(skip)]
@ -25,17 +35,29 @@ pub struct Settings {
pub debug_outlines: bool,
}
fn to_gamepad_id(raw_id: usize) -> GamepadId {
unsafe {
std::mem::transmute(raw_id)
}
}
impl Settings {
pub fn load(_ctx: &mut Context) -> GameResult<Settings> {
Ok(Settings::default())
}
pub fn create_player1_controller(&self) -> Box<dyn PlayerController> {
Box::new(KeyboardController::new(TargetPlayer::Player1))
match self.player1_controller_type {
ControllerType::Keyboard => Box::new(KeyboardController::new(TargetPlayer::Player1)),
ControllerType::Gamepad(id) => Box::new(GilrsPlayerController::new(to_gamepad_id(id))),
}
}
pub fn create_player2_controller(&self) -> Box<dyn PlayerController> {
Box::new(KeyboardController::new(TargetPlayer::Player2))
match self.player2_controller_type {
ControllerType::Keyboard => Box::new(KeyboardController::new(TargetPlayer::Player2)),
ControllerType::Gamepad(id) => Box::new(GilrsPlayerController::new(to_gamepad_id(id))),
}
}
}
@ -49,6 +71,8 @@ impl Default for Settings {
touch_controls: cfg!(target_os = "android"),
player1_key_map: p1_default_keymap(),
player2_key_map: p2_default_keymap(),
player1_controller_type: ControllerType::Keyboard,
player2_controller_type: ControllerType::Keyboard,
speed: 1.0,
god_mode: false,
infinite_booster: false,

View file

@ -6,6 +6,7 @@ use chrono::{Datelike, Local};
use ggez::{Context, filesystem, GameResult, graphics};
use ggez::filesystem::OpenOptions;
use ggez::graphics::Canvas;
use gilrs::Gilrs;
use num_traits::clamp;
use crate::bmfont_renderer::BMFontRenderer;
@ -18,7 +19,7 @@ use crate::profile::GameProfile;
use crate::rng::RNG;
use crate::scene::game_scene::GameScene;
use crate::scene::Scene;
use crate::settings::Settings;
use crate::settings::{Settings, ControllerType};
use crate::shaders::Shaders;
use crate::sound::SoundManager;
use crate::stage::StageData;
@ -108,6 +109,7 @@ pub struct SharedGameState {
pub canvas_size: (f32, f32),
pub screen_size: (f32, f32),
pub next_scene: Option<Box<dyn Scene>>,
pub gilrs: Option<Gilrs>,
pub textscript_vm: TextScriptVM,
pub season: Season,
pub constants: EngineConstants,
@ -128,7 +130,18 @@ impl SharedGameState {
let mut constants = EngineConstants::defaults();
let mut base_path = "/";
let settings = Settings::load(ctx)?;
let mut settings = Settings::load(ctx)?;
let mut gilrs = Gilrs::new().ok();
if let Some(gilrs) = gilrs.as_mut() {
for (id, pad) in gilrs.gamepads() {
log::info!("Found gamepad: {} ({})", pad.name(), id);
}
if let Some((id, _)) = gilrs.gamepads().next() {
settings.player2_controller_type = ControllerType::Gamepad(id.into());
}
}
if filesystem::exists(ctx, "/base/Nicalis.bmp") {
info!("Cave Story+ (PC) data files detected.");
@ -181,6 +194,7 @@ impl SharedGameState {
screen_size,
canvas_size,
next_scene: None,
gilrs,
textscript_vm: TextScriptVM::new(),
season,
constants,

View file

@ -351,8 +351,6 @@ impl StageData {
let npc2 = from_shift_jis(&npc2_buf[0..zero_index(&npc2_buf)]);
let name = from_shift_jis(&name_buf[0..zero_index(&name_buf)]);
println!("bg type: {}", bg_type);
let stage = StageData {
name: name.clone(),
map: map.clone(),