Skip to content

Commit

Permalink
added support for gamepad
Browse files Browse the repository at this point in the history
  • Loading branch information
PsichiX committed Jan 26, 2024
1 parent 8632a2c commit 00f7e74
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 5 deletions.
9 changes: 5 additions & 4 deletions Cargo.toml
Expand Up @@ -3,7 +3,7 @@ members = ["templates/*"]

[package]
name = "micro-games-kit"
version = "0.24.0"
version = "0.24.1"
edition = "2021"
authors = ["Patryk 'PsichiX' Budzynski <psichix@gmail.com>"]
description = "Micro Games Kit"
Expand All @@ -21,9 +21,9 @@ spitfire-fontdue = "0.28"
spitfire-draw = "0.28"
spitfire-input = "0.28"
spitfire-gui = "0.28"
raui-core = "0.62"
raui-immediate = "0.62"
raui-immediate-widgets = "0.62"
raui-core = "0.63"
raui-immediate = "0.63"
raui-immediate-widgets = "0.63"
emergent = "1.7"
typid = "1"
image = "0.24"
Expand All @@ -35,6 +35,7 @@ noise = "0.8"
rand = "0.8"
kira = "0.8"
rstar = "0.11"
gilrs = "0.10"

[target.'cfg(target_arch = "wasm32")'.dependencies]
winit = "0.26"
Expand Down
3 changes: 2 additions & 1 deletion src/game.rs
@@ -1,4 +1,4 @@
use crate::context::GameContext;
use crate::{context::GameContext, gamepad::GamepadInput};
#[cfg(not(target_arch = "wasm32"))]
use glutin::{event::Event, window::Window};
#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -200,6 +200,7 @@ impl GameInstance {
);
self.draw.end_frame();
if !self.input_maintain_on_fixed_step || fixed_step {
GamepadInput::maintain();
self.input.maintain();
}

Expand Down
224 changes: 224 additions & 0 deletions src/gamepad.rs
@@ -0,0 +1,224 @@
use gilrs::{Axis, Button, GamepadId, Gilrs};
use spitfire_input::{InputActionOrAxisRef, InputAxis};
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
};

thread_local! {
static INSTANCE: Option<RefCell<Gilrs>> = Gilrs::new().ok().map(RefCell::new);
static GAMEPADS: RefCell<HashSet<GamepadId>> = Default::default();
}

#[derive(Debug, Clone)]
pub enum GamepadInputAxis {
Single {
input: InputActionOrAxisRef,
deadzone: f32,
},
Double {
negative: InputActionOrAxisRef,
positive: InputActionOrAxisRef,
deadzone: f32,
},
}

impl GamepadInputAxis {
pub fn single(input: impl Into<InputActionOrAxisRef>, deadzone: f32) -> Self {
Self::Single {
input: input.into(),
deadzone,
}
}

pub fn double(
negative: impl Into<InputActionOrAxisRef>,
positive: impl Into<InputActionOrAxisRef>,
deadzone: f32,
) -> Self {
Self::Double {
negative: negative.into(),
positive: positive.into(),
deadzone,
}
}
}

#[derive(Debug, Default, Clone)]
pub struct GamepadInput {
id: Option<GamepadId>,
pub buttons: HashMap<Button, InputActionOrAxisRef>,
pub axes: HashMap<Axis, GamepadInputAxis>,
pub auto_acquire: bool,
}

impl Drop for GamepadInput {
fn drop(&mut self) {
self.release();
}
}

impl GamepadInput {
pub fn is_supported() -> bool {
INSTANCE
.try_with(|instance| instance.is_some())
.unwrap_or_default()
}

pub fn new() -> Option<Self> {
let mut result = Self::default();
if result.acquire() {
Some(result)
} else {
None
}
}

pub fn acquire(&mut self) -> bool {
self.release();
INSTANCE
.try_with(|instance| {
if let Some(instance) = instance {
let instance = instance.borrow();
return GAMEPADS
.try_with(|gamepads| {
let mut gamepads = gamepads.borrow_mut();
for (id, _) in instance.gamepads() {
if !gamepads.contains(&id) {
gamepads.insert(id);
self.id = Some(id);
return true;
}
}
false
})
.unwrap_or_default();
}
false
})
.unwrap_or_default()
}

pub fn release(&mut self) {
let _ = GAMEPADS.try_with(|gamepads| {
if let Some(id) = self.id {
gamepads.borrow_mut().remove(&id);
}
});
self.id = None;
}

pub fn id(&self) -> Option<GamepadId> {
self.id
}

pub fn is_connected(&self) -> bool {
self.id.is_some()
}

pub fn button(mut self, id: Button, input: impl Into<InputActionOrAxisRef>) -> Self {
self.buttons.insert(id, input.into());
self
}

pub fn axis(mut self, id: Axis, input: impl Into<GamepadInputAxis>) -> Self {
self.axes.insert(id, input.into());
self
}

pub fn auto_acquire(mut self) -> Self {
self.auto_acquire = true;
self
}

pub fn maintain() {
let _ = INSTANCE.try_with(|instance| {
if let Some(instance) = instance {
let mut instance = instance.borrow_mut();
while instance.next_event().is_some() {}
}
});
}

pub fn apply(&mut self) {
if self.auto_acquire && !self.is_connected() {
self.acquire();
}
if let Some(id) = self.id {
let _ = INSTANCE.try_with(|instance| {
if let Some(instance) = instance {
let instance = instance.borrow();
let _ = GAMEPADS.try_with(|gamepads| {
if let Some(gamepad) = instance.connected_gamepad(id) {
for (id, input) in &mut self.buttons {
if let Some(data) = gamepad.button_data(*id) {
match input {
InputActionOrAxisRef::Action(input) => {
input.set(input.get().change(data.is_pressed()));
}
InputActionOrAxisRef::Axis(input) => {
input.set(InputAxis(data.value()));
}
_ => {}
}
}
}
for (id, input) in &mut self.axes {
if let Some(data) = gamepad.axis_data(*id) {
match input {
GamepadInputAxis::Single { input, deadzone } => {
let mut value = data.value().abs();
if value < *deadzone {
value = 0.0;
}
match input {
InputActionOrAxisRef::Action(input) => {
input.set(input.get().change(value > 0.5));
}
InputActionOrAxisRef::Axis(input) => {
input.set(InputAxis(value));
}
_ => {}
}
}
GamepadInputAxis::Double {
negative,
positive,
deadzone,
} => {
let mut value = data.value();
if value.abs() < *deadzone {
value = 0.0;
}
match positive {
InputActionOrAxisRef::Action(input) => {
input.set(input.get().change(value > 0.5));
}
InputActionOrAxisRef::Axis(input) => {
input.set(InputAxis(value.max(0.0)));
}
_ => {}
}
match negative {
InputActionOrAxisRef::Action(input) => {
input.set(input.get().change(value < -0.5));
}
InputActionOrAxisRef::Axis(input) => {
input.set(InputAxis(-value.min(0.0)));
}
_ => {}
}
}
}
}
}
} else {
gamepads.borrow_mut().remove(&id);
self.id = None;
}
});
}
});
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
@@ -1,6 +1,7 @@
pub mod third_party {
pub use emergent;
pub use fontdue;
pub use gilrs;
#[cfg(not(target_arch = "wasm32"))]
pub use glutin as windowing;
pub use image;
Expand Down Expand Up @@ -34,6 +35,7 @@ pub mod character;
pub mod config;
pub mod context;
pub mod game;
pub mod gamepad;
pub mod grid_world;
pub mod loader;
pub mod pcg;
Expand Down
18 changes: 18 additions & 0 deletions templates/top-down/src/game/player/mod.rs
Expand Up @@ -17,8 +17,10 @@ use micro_games_kit::{
character::{Character, CharacterController},
context::GameContext,
game::GameObject,
gamepad::{GamepadInput, GamepadInputAxis},
third_party::{
emergent::builders::behavior_tree::BehaviorTree,
gilrs::{Axis, Button},
spitfire_draw::{
sprite::{Sprite, SpriteTexture},
utils::{Drawable, ShaderRef, TextureRef},
Expand Down Expand Up @@ -73,6 +75,7 @@ pub struct PlayerInputState {
pub attack: InputActionRef,
pub weapon_prev: InputActionRef,
pub weapon_next: InputActionRef,
pub gamepad: GamepadInput,
}

pub struct PlayerState {
Expand Down Expand Up @@ -112,6 +115,8 @@ impl GameObject for PlayerState {
}

fn process(&mut self, context: &mut GameContext, delta_time: f32) {
self.input.gamepad.apply();

if self.input.weapon_prev.get().is_pressed() {
self.weapon = self.weapon.prev();
}
Expand Down Expand Up @@ -160,6 +165,19 @@ impl PlayerState {
state.input.movement =
CardinalInputCombinator::new(left.clone(), right.clone(), up.clone(), down.clone());
state.sprite.transform.position = position.into();
state.input.gamepad = GamepadInput::default()
.auto_acquire()
.axis(
Axis::LeftStickX,
GamepadInputAxis::double(left.clone(), right.clone(), 0.1),
)
.axis(
Axis::LeftStickY,
GamepadInputAxis::double(down.clone(), up.clone(), 0.1),
)
.button(Button::South, state.input.attack.clone())
.button(Button::West, state.input.weapon_prev.clone())
.button(Button::North, state.input.weapon_next.clone());

let mapping = InputMapping::default()
.action(VirtualAction::KeyButton(VirtualKeyCode::A), left)
Expand Down

0 comments on commit 00f7e74

Please sign in to comment.