diff --git a/Cargo.toml b/Cargo.toml index a38db3beae0c0..c36940d77fda8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -230,6 +230,10 @@ path = "examples/input/keyboard_input_events.rs" name = "gamepad_input" path = "examples/input/gamepad_input.rs" +[[example]] +name = "gamepad_input_event" +path = "examples/input/gamepad_input_event.rs" + [[example]] name = "touch_input" path = "examples/input/touch_input.rs" diff --git a/crates/bevy_gilrs/src/gilrs_system.rs b/crates/bevy_gilrs/src/gilrs_system.rs index 22bb3b527583e..f448fdeb2aee8 100644 --- a/crates/bevy_gilrs/src/gilrs_system.rs +++ b/crates/bevy_gilrs/src/gilrs_system.rs @@ -2,88 +2,52 @@ use crate::converter::{convert_axis, convert_button, convert_gamepad_id}; use bevy_app::Events; use bevy_ecs::{Resources, World}; use bevy_input::prelude::*; -use gilrs::{Button, EventType, Gilrs}; +use gilrs::{EventType, Gilrs}; -pub fn gilrs_startup_system(_world: &mut World, resources: &mut Resources) { +pub fn gilrs_event_startup_system(_world: &mut World, resources: &mut Resources) { let gilrs = resources.get_thread_local::().unwrap(); - let mut gamepad_event = resources.get_mut::>().unwrap(); - let mut inputs = resources.get_mut::>().unwrap(); - let mut axes = resources.get_mut::>().unwrap(); - let mut button_axes = resources.get_mut::>().unwrap(); - gamepad_event.update(); - inputs.update(); - for (gilrs_id, gilrs_gamepad) in gilrs.gamepads() { - connect_gamepad( - gilrs_gamepad, - convert_gamepad_id(gilrs_id), - &mut gamepad_event, - &mut inputs, - &mut axes, - &mut button_axes, - ); + let mut event = resources.get_mut::>().unwrap(); + event.update(); + for (id, _) in gilrs.gamepads() { + event.send(GamepadEvent( + convert_gamepad_id(id), + GamepadEventType::Connected, + )); } } -pub fn gilrs_update_system(_world: &mut World, resources: &mut Resources) { +pub fn girls_event_system(_world: &mut World, resources: &mut Resources) { let mut gilrs = resources.get_thread_local_mut::().unwrap(); - let mut gamepad_event = resources.get_mut::>().unwrap(); - let mut inputs = resources.get_mut::>().unwrap(); - let mut axes = resources.get_mut::>().unwrap(); - let mut button_axes = resources.get_mut::>().unwrap(); - - gamepad_event.update(); - inputs.update(); + let mut event = resources.get_mut::>().unwrap(); + event.update(); while let Some(gilrs_event) = gilrs.next_event() { match gilrs_event.event { EventType::Connected => { - connect_gamepad( - gilrs.gamepad(gilrs_event.id), + event.send(GamepadEvent( convert_gamepad_id(gilrs_event.id), - &mut gamepad_event, - &mut inputs, - &mut axes, - &mut button_axes, - ); + GamepadEventType::Connected, + )); } EventType::Disconnected => { - disconnect_gamepad( + event.send(GamepadEvent( convert_gamepad_id(gilrs_event.id), - &mut gamepad_event, - &mut inputs, - &mut axes, - &mut button_axes, - ); + GamepadEventType::Disconnected, + )); } - EventType::ButtonPressed(gilrs_button, _) => { - if let Some(button_type) = convert_button(gilrs_button) { - inputs.press(GamepadButton( - convert_gamepad_id(gilrs_event.id), - button_type, - )); - } - } - EventType::ButtonReleased(gilrs_button, _) => { + EventType::ButtonChanged(gilrs_button, value, _) => { if let Some(button_type) = convert_button(gilrs_button) { - inputs.release(GamepadButton( + event.send(GamepadEvent( convert_gamepad_id(gilrs_event.id), - button_type, + GamepadEventType::ButtonChanged(button_type, value), )); } } - EventType::ButtonChanged(gilrs_button, value, _) => { - if let Some(button_type) = convert_button(gilrs_button) { - button_axes.set( - GamepadButton(convert_gamepad_id(gilrs_event.id), button_type), - value, - ); - } - } EventType::AxisChanged(gilrs_axis, value, _) => { if let Some(axis_type) = convert_axis(gilrs_axis) { - axes.set( - GamepadAxis(convert_gamepad_id(gilrs_event.id), axis_type), - value, - ); + event.send(GamepadEvent( + convert_gamepad_id(gilrs_event.id), + GamepadEventType::AxisChanged(axis_type, value), + )); } } _ => (), @@ -91,88 +55,3 @@ pub fn gilrs_update_system(_world: &mut World, resources: &mut Resources) { } gilrs.inc(); } - -const ALL_GILRS_BUTTONS: [Button; 19] = [ - Button::South, - Button::East, - Button::North, - Button::West, - Button::C, - Button::Z, - Button::LeftTrigger, - Button::LeftTrigger2, - Button::RightTrigger, - Button::RightTrigger2, - Button::Select, - Button::Start, - Button::Mode, - Button::LeftThumb, - Button::RightThumb, - Button::DPadUp, - Button::DPadDown, - Button::DPadLeft, - Button::DPadRight, -]; - -const ALL_GILRS_AXES: [gilrs::Axis; 8] = [ - gilrs::Axis::LeftStickX, - gilrs::Axis::LeftStickY, - gilrs::Axis::LeftZ, - gilrs::Axis::RightStickX, - gilrs::Axis::RightStickY, - gilrs::Axis::RightZ, - gilrs::Axis::DPadX, - gilrs::Axis::DPadY, -]; - -fn connect_gamepad( - gilrs_gamepad: gilrs::Gamepad, - gamepad: Gamepad, - events: &mut Events, - inputs: &mut Input, - axes: &mut Axis, - button_axes: &mut Axis, -) { - for gilrs_button in ALL_GILRS_BUTTONS.iter() { - if let Some(button_type) = convert_button(*gilrs_button) { - if let Some(button_data) = gilrs_gamepad.button_data(*gilrs_button) { - let gamepad_button = GamepadButton(gamepad, button_type); - inputs.reset(gamepad_button); - if button_data.is_pressed() { - inputs.press(gamepad_button); - } - button_axes.set(gamepad_button, button_data.value()); - } - } - } - for gilrs_axis in ALL_GILRS_AXES.iter() { - if let Some(axis_type) = convert_axis(*gilrs_axis) { - let gamepad_axis = GamepadAxis(gamepad, axis_type); - axes.set(gamepad_axis, gilrs_gamepad.value(*gilrs_axis)); - } - } - events.send(GamepadEvent(gamepad, GamepadEventType::Connected)); -} - -fn disconnect_gamepad( - gamepad: Gamepad, - events: &mut Events, - inputs: &mut Input, - axes: &mut Axis, - button_axes: &mut Axis, -) { - for gilrs_button in ALL_GILRS_BUTTONS.iter() { - if let Some(button_type) = convert_button(*gilrs_button) { - let gamepad_button = GamepadButton(gamepad, button_type); - inputs.reset(gamepad_button); - button_axes.remove(&gamepad_button); - } - } - for gilrs_axis in ALL_GILRS_AXES.iter() { - if let Some(axis_type) = convert_axis(*gilrs_axis) { - let gamepad_axis = GamepadAxis(gamepad, axis_type); - axes.remove(&gamepad_axis); - } - } - events.send(GamepadEvent(gamepad, GamepadEventType::Disconnected)); -} diff --git a/crates/bevy_gilrs/src/lib.rs b/crates/bevy_gilrs/src/lib.rs index 8edce92f7956f..99f803f50fa3f 100644 --- a/crates/bevy_gilrs/src/lib.rs +++ b/crates/bevy_gilrs/src/lib.rs @@ -3,21 +3,23 @@ mod gilrs_system; use bevy_app::prelude::*; use bevy_ecs::prelude::*; -use gilrs_system::{gilrs_startup_system, gilrs_update_system}; +use gilrs::GilrsBuilder; +use gilrs_system::{gilrs_event_startup_system, girls_event_system}; #[derive(Default)] pub struct GilrsPlugin; impl Plugin for GilrsPlugin { fn build(&self, app: &mut AppBuilder) { - match gilrs::Gilrs::new() { + match GilrsBuilder::new() + .with_default_filters(false) + .set_update_state(false) + .build() + { Ok(gilrs) => { app.add_thread_local_resource(gilrs) - .add_startup_system(gilrs_startup_system.thread_local_system()) - .add_system_to_stage( - stage::EVENT_UPDATE, - gilrs_update_system.thread_local_system(), - ); + .add_startup_system(gilrs_event_startup_system.thread_local_system()) + .add_system_to_stage(stage::FIRST, girls_event_system.thread_local_system()); } Err(err) => log::error!("Failed to start Gilrs. {}", err), } diff --git a/crates/bevy_input/src/axis.rs b/crates/bevy_input/src/axis.rs index df1ff289d62c1..e1863969e8926 100644 --- a/crates/bevy_input/src/axis.rs +++ b/crates/bevy_input/src/axis.rs @@ -24,11 +24,11 @@ where self.axis_data.insert(axis, value) } - pub fn get(&self, axis: &T) -> Option { - self.axis_data.get(axis).copied() + pub fn get(&self, axis: T) -> Option { + self.axis_data.get(&axis).copied() } - pub fn remove(&mut self, axis: &T) -> Option { - self.axis_data.remove(axis) + pub fn remove(&mut self, axis: T) -> Option { + self.axis_data.remove(&axis) } } diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 48aa5e22831f9..bd416935987c5 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -1,15 +1,22 @@ +use crate::{Axis, Input}; +use bevy_app::{EventReader, Events}; +use bevy_ecs::{Local, Res, ResMut}; +use bevy_utils::HashMap; + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct Gamepad(pub usize); -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum GamepadEventType { Connected, Disconnected, + ButtonChanged(GamepadButtonType, f32), + AxisChanged(GamepadAxisType, f32), } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct GamepadEvent(pub Gamepad, pub GamepadEventType); @@ -57,3 +64,230 @@ pub enum GamepadAxisType { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct GamepadAxis(pub Gamepad, pub GamepadAxisType); + +#[derive(Default, Debug)] +pub struct GamepadSetting { + pub default_button_setting: ButtonSetting, + pub default_axis_setting: AxisSetting, + pub default_button_axis_setting: ButtonAxisSetting, + pub button_settings: HashMap, + pub axis_settings: HashMap, + pub button_axis_settings: HashMap, +} + +impl GamepadSetting { + pub fn get_button_setting(&self, button: GamepadButton) -> &ButtonSetting { + self.button_settings + .get(&button) + .unwrap_or(&self.default_button_setting) + } + + pub fn get_axis_setting(&self, axis: GamepadAxis) -> &AxisSetting { + self.axis_settings + .get(&axis) + .unwrap_or(&self.default_axis_setting) + } + + pub fn get_button_axis_setting(&self, button: GamepadButton) -> &ButtonAxisSetting { + self.button_axis_settings + .get(&button) + .unwrap_or(&self.default_button_axis_setting) + } +} + +#[derive(Debug, Clone)] +pub struct ButtonSetting { + pub press: f32, + pub release: f32, +} + +impl Default for ButtonSetting { + fn default() -> Self { + ButtonSetting { + press: 0.75, + release: 0.65, + } + } +} + +impl ButtonSetting { + fn is_pressed(&self, value: f32) -> bool { + value >= self.press + } + + fn is_released(&self, value: f32) -> bool { + value <= self.release + } +} + +#[derive(Debug, Clone)] +pub struct AxisSetting { + pub positive_high: f32, + pub positive_low: f32, + pub negative_high: f32, + pub negative_low: f32, + pub threshold: f32, +} + +impl Default for AxisSetting { + fn default() -> Self { + AxisSetting { + positive_high: 0.95, + positive_low: 0.05, + negative_high: -0.95, + negative_low: -0.05, + threshold: 0.01, + } + } +} + +impl AxisSetting { + fn filter(&self, new_value: f32, old_value: Option) -> f32 { + if let Some(old_value) = old_value { + if (new_value - old_value).abs() <= self.threshold { + return old_value; + } + } + if new_value <= self.positive_low && new_value >= self.negative_low { + return 0.0; + } + if new_value >= self.positive_high { + return 1.0; + } + if new_value <= self.negative_high { + return -1.0; + } + new_value + } +} + +#[derive(Debug, Clone)] +pub struct ButtonAxisSetting { + pub high: f32, + pub low: f32, + pub threshold: f32, +} + +impl Default for ButtonAxisSetting { + fn default() -> Self { + ButtonAxisSetting { + high: 0.95, + low: 0.05, + threshold: 0.01, + } + } +} + +impl ButtonAxisSetting { + fn filter(&self, new_value: f32, old_value: Option) -> f32 { + if let Some(old_value) = old_value { + if (new_value - old_value).abs() <= self.threshold { + return old_value; + } + } + if new_value <= self.low { + return 0.0; + } + if new_value >= self.high { + return 1.0; + } + new_value + } +} + +#[derive(Default)] +pub struct GamepadEventState { + gamepad_event_reader: EventReader, +} + +pub fn gamepad_event_system( + mut state: Local, + mut button_input: ResMut>, + mut axis: ResMut>, + mut button_axis: ResMut>, + events: Res>, + settings: Res, +) { + button_input.update(); + for event in state.gamepad_event_reader.iter(&events) { + let (gamepad, event) = (&event.0, &event.1); + match event { + GamepadEventType::Connected => { + for button_type in ALL_BUTTON_TYPES.iter() { + let gamepad_button = GamepadButton(*gamepad, *button_type); + button_input.reset(gamepad_button); + button_axis.set(gamepad_button, 0.0); + } + for axis_type in ALL_AXIS_TYPES.iter() { + axis.set(GamepadAxis(*gamepad, *axis_type), 0.0); + } + } + GamepadEventType::Disconnected => { + for button_type in ALL_BUTTON_TYPES.iter() { + let gamepad_button = GamepadButton(*gamepad, *button_type); + button_input.reset(gamepad_button); + button_axis.remove(gamepad_button); + } + for axis_type in ALL_AXIS_TYPES.iter() { + axis.remove(GamepadAxis(*gamepad, *axis_type)); + } + } + GamepadEventType::AxisChanged(axis_type, value) => { + let gamepad_axis = GamepadAxis(*gamepad, *axis_type); + let value = settings + .get_axis_setting(gamepad_axis) + .filter(*value, axis.get(gamepad_axis)); + axis.set(gamepad_axis, value); + } + GamepadEventType::ButtonChanged(button_type, value) => { + let gamepad_button = GamepadButton(*gamepad, *button_type); + let filtered_value = settings + .get_button_axis_setting(gamepad_button) + .filter(*value, button_axis.get(gamepad_button)); + button_axis.set(gamepad_button, filtered_value); + + let button_property = settings.get_button_setting(gamepad_button); + if button_input.pressed(gamepad_button) { + if button_property.is_released(*value) { + button_input.release(gamepad_button); + } + } else if button_property.is_pressed(*value) { + button_input.press(gamepad_button); + } + } + } + } +} + +const ALL_BUTTON_TYPES: [GamepadButtonType; 19] = [ + GamepadButtonType::South, + GamepadButtonType::East, + GamepadButtonType::North, + GamepadButtonType::West, + GamepadButtonType::C, + GamepadButtonType::Z, + GamepadButtonType::LeftTrigger, + GamepadButtonType::LeftTrigger2, + GamepadButtonType::RightTrigger, + GamepadButtonType::RightTrigger2, + GamepadButtonType::Select, + GamepadButtonType::Start, + GamepadButtonType::Mode, + GamepadButtonType::LeftThumb, + GamepadButtonType::RightThumb, + GamepadButtonType::DPadUp, + GamepadButtonType::DPadDown, + GamepadButtonType::DPadLeft, + GamepadButtonType::DPadRight, +]; + +const ALL_AXIS_TYPES: [GamepadAxisType; 8] = [ + GamepadAxisType::LeftStickX, + GamepadAxisType::LeftStickY, + GamepadAxisType::LeftZ, + GamepadAxisType::RightStickX, + GamepadAxisType::RightStickY, + GamepadAxisType::RightZ, + GamepadAxisType::DPadX, + GamepadAxisType::DPadY, +]; diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 7520779651a21..6c528c2dda28c 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -27,7 +27,7 @@ use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotio use touch::{touch_screen_input_system, TouchInput, Touches}; use bevy_ecs::IntoQuerySystem; -use gamepad::{GamepadAxis, GamepadButton, GamepadEvent}; +use gamepad::{gamepad_event_system, GamepadAxis, GamepadButton, GamepadEvent, GamepadSetting}; /// Adds keyboard and mouse input to an App #[derive(Default)] @@ -50,9 +50,15 @@ impl Plugin for InputPlugin { mouse_button_input_system.system(), ) .add_event::() + .init_resource::() .init_resource::>() .init_resource::>() .init_resource::>() + .add_startup_system_to_stage( + bevy_app::startup_stage::POST_STARTUP, + gamepad_event_system.system(), + ) + .add_system_to_stage(bevy_app::stage::EVENT_UPDATE, gamepad_event_system.system()) .add_event::() .init_resource::() .add_system_to_stage( diff --git a/examples/input/gamepad_input.rs b/examples/input/gamepad_input.rs index 5ddd40b2f86a8..768f90b358211 100644 --- a/examples/input/gamepad_input.rs +++ b/examples/input/gamepad_input.rs @@ -1,15 +1,14 @@ use bevy::prelude::*; use bevy_input::gamepad::{Gamepad, GamepadButton, GamepadEvent, GamepadEventType}; -use std::collections::HashSet; +use bevy_utils::HashSet; fn main() { App::build() .add_default_plugins() + .init_resource::() .add_startup_system(connection_system.system()) .add_system(connection_system.system()) - .add_system(button_system.system()) - .add_system(axis_system.system()) - .init_resource::() + .add_system(gamepad_system.system()) .run(); } @@ -24,85 +23,49 @@ fn connection_system(mut lobby: ResMut, gamepad_event: Res { lobby.gamepads.insert(*gamepad); - println!("Connected {:?}", gamepad); + println!("{:?} Connected", gamepad); } GamepadEvent(gamepad, GamepadEventType::Disconnected) => { lobby.gamepads.remove(gamepad); - println!("Disconnected {:?}", gamepad); + println!("{:?} Disconnected", gamepad); } + _ => (), } } } -fn button_system( +fn gamepad_system( lobby: Res, - inputs: Res>, + button_inputs: Res>, button_axes: Res>, + axes: Res>, ) { - let button_types = [ - GamepadButtonType::South, - GamepadButtonType::East, - GamepadButtonType::North, - GamepadButtonType::West, - GamepadButtonType::C, - GamepadButtonType::Z, - GamepadButtonType::LeftTrigger, - GamepadButtonType::LeftTrigger2, - GamepadButtonType::RightTrigger, - GamepadButtonType::RightTrigger2, - GamepadButtonType::Select, - GamepadButtonType::Start, - GamepadButtonType::Mode, - GamepadButtonType::LeftThumb, - GamepadButtonType::RightThumb, - GamepadButtonType::DPadUp, - GamepadButtonType::DPadDown, - GamepadButtonType::DPadLeft, - GamepadButtonType::DPadRight, - ]; for gamepad in lobby.gamepads.iter() { - for button_type in button_types.iter() { - if inputs.just_pressed(GamepadButton(*gamepad, *button_type)) { - println!("Pressed {:?}", GamepadButton(*gamepad, *button_type)); - } else if inputs.just_released(GamepadButton(*gamepad, *button_type)) { - println!("Released {:?}", GamepadButton(*gamepad, *button_type)); - } - if let Some(value) = button_axes.get(&GamepadButton(*gamepad, *button_type)) { - if value_check(value) { - println!( - "Button as Axis {:?} is {}", - GamepadButton(*gamepad, *button_type), - value - ); - } - } + let south_button = GamepadButton(*gamepad, GamepadButtonType::South); + if button_inputs.just_pressed(south_button) { + println!( + "{:?} of {:?} is just pressed", + GamepadButtonType::South, + gamepad + ); + } else if button_inputs.just_released(south_button) { + println!( + "{:?} of {:?} is just released", + GamepadButtonType::South, + gamepad + ); } - } -} -fn axis_system(lobby: Res, axes: Res>) { - let axis_types = [ - GamepadAxisType::LeftStickX, - GamepadAxisType::LeftStickY, - GamepadAxisType::LeftZ, - GamepadAxisType::RightStickX, - GamepadAxisType::RightStickY, - GamepadAxisType::RightZ, - GamepadAxisType::DPadX, - GamepadAxisType::DPadY, - ]; - for gamepad in lobby.gamepads.iter() { - for axis_type in axis_types.iter() { - if let Some(value) = axes.get(&GamepadAxis(*gamepad, *axis_type)) { - if value_check(value) { - println!("Axis {:?} is {}", GamepadAxis(*gamepad, *axis_type), value); - } - } - } + println!( + "For {:?}: {:?} is {:.4}, {:?} is {:.4}", + gamepad, + GamepadButtonType::RightTrigger2, + button_axes + .get(GamepadButton(*gamepad, GamepadButtonType::RightTrigger2)) + .unwrap_or(0.0), + GamepadAxisType::LeftStickX, + axes.get(GamepadAxis(*gamepad, GamepadAxisType::LeftStickX)) + .unwrap_or(0.0) + ) } } - -fn value_check(value: f32) -> bool { - let value = value.abs(); - value > 0.1f32 && value < 0.9f32 -} diff --git a/examples/input/gamepad_input_event.rs b/examples/input/gamepad_input_event.rs new file mode 100644 index 0000000000000..4052bd62fe55b --- /dev/null +++ b/examples/input/gamepad_input_event.rs @@ -0,0 +1,34 @@ +use bevy::prelude::*; +use bevy_input::gamepad::{GamepadEvent, GamepadEventType}; + +fn main() { + App::build() + .add_default_plugins() + .add_startup_system(gamepad_raw_events.system()) + .add_system(gamepad_raw_events.system()) + .run(); +} + +#[derive(Default)] +struct State { + gamepad_event_reader: EventReader, +} + +fn gamepad_raw_events(mut state: Local, gamepad_event: Res>) { + for event in state.gamepad_event_reader.iter(&gamepad_event) { + match &event { + GamepadEvent(gamepad, GamepadEventType::Connected) => { + println!("{:?} Connected", gamepad); + } + GamepadEvent(gamepad, GamepadEventType::Disconnected) => { + println!("{:?} Disconnected", gamepad); + } + GamepadEvent(gamepad, GamepadEventType::ButtonChanged(button_type, value)) => { + println!("{:?} of {:?} is changed to {}", button_type, gamepad, value); + } + GamepadEvent(gamepad, GamepadEventType::AxisChanged(axis_type, value)) => { + println!("{:?} of {:?} is changed to {}", axis_type, gamepad, value); + } + } + } +}