Skip to content

Commit

Permalink
gamepad: expose raw and filtered gamepad events. (#711)
Browse files Browse the repository at this point in the history
  • Loading branch information
cart authored Oct 21, 2020
1 parent 894cc5e commit 267599e
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 131 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ name = "gamepad_input"
path = "examples/input/gamepad_input.rs"

[[example]]
name = "gamepad_input_event"
path = "examples/input/gamepad_input_event.rs"
name = "gamepad_input_events"
path = "examples/input/gamepad_input_events.rs"

[[example]]
name = "touch_input"
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_app/src/app_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ impl AppBuilder {
.add_startup_stage(startup_stage::STARTUP)
.add_startup_stage(startup_stage::POST_STARTUP)
.add_stage(stage::FIRST)
.add_stage(stage::EVENT_UPDATE)
.add_stage(stage::PRE_EVENT)
.add_stage(stage::EVENT)
.add_stage(stage::PRE_UPDATE)
.add_stage(stage::UPDATE)
.add_stage(stage::POST_UPDATE)
Expand Down Expand Up @@ -217,7 +218,7 @@ impl AppBuilder {
T: Send + Sync + 'static,
{
self.add_resource(Events::<T>::default())
.add_system_to_stage(stage::EVENT_UPDATE, Events::<T>::update_system.system())
.add_system_to_stage(stage::EVENT, Events::<T>::update_system.system())
}

pub fn add_resource<T>(&mut self, resource: T) -> &mut Self
Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_app/src/stage.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/// Name of app stage that runs before all other app stages
pub const FIRST: &str = "first";

/// Name of app stage that updates events. Generally this should run before UPDATE
pub const EVENT_UPDATE: &str = "event_update";
/// Name of app stage that runs before EVENT
pub const PRE_EVENT: &str = "pre_events";

/// Name of app stage that updates events. Runs before UPDATE
pub const EVENT: &str = "events";

/// Name of app stage responsible for performing setup before an update. Runs before UPDATE.
pub const PRE_UPDATE: &str = "pre_update";
Expand Down
26 changes: 7 additions & 19 deletions crates/bevy_gilrs/src/gilrs_system.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,38 @@
use crate::converter::{convert_axis, convert_button, convert_gamepad_id};
use bevy_app::Events;
use bevy_ecs::{Resources, World};
use bevy_input::prelude::*;
use bevy_input::{gamepad::GamepadEventRaw, prelude::*};
use gilrs::{EventType, Gilrs};

pub fn gilrs_event_startup_system(_world: &mut World, resources: &mut Resources) {
let gilrs = resources.get_thread_local::<Gilrs>().unwrap();
let mut event = resources.get_mut::<Events<GamepadEvent>>().unwrap();
event.update();
for (id, _) in gilrs.gamepads() {
event.send(GamepadEvent(
convert_gamepad_id(id),
GamepadEventType::Connected,
));
}
}

pub fn girls_event_system(_world: &mut World, resources: &mut Resources) {
pub fn gilrs_event_system(_world: &mut World, resources: &mut Resources) {
let mut gilrs = resources.get_thread_local_mut::<Gilrs>().unwrap();
let mut event = resources.get_mut::<Events<GamepadEvent>>().unwrap();
let mut event = resources.get_mut::<Events<GamepadEventRaw>>().unwrap();
event.update();
while let Some(gilrs_event) = gilrs.next_event() {
match gilrs_event.event {
EventType::Connected => {
event.send(GamepadEvent(
event.send(GamepadEventRaw(
convert_gamepad_id(gilrs_event.id),
GamepadEventType::Connected,
));
}
EventType::Disconnected => {
event.send(GamepadEvent(
event.send(GamepadEventRaw(
convert_gamepad_id(gilrs_event.id),
GamepadEventType::Disconnected,
));
}
EventType::ButtonChanged(gilrs_button, value, _) => {
if let Some(button_type) = convert_button(gilrs_button) {
event.send(GamepadEvent(
event.send(GamepadEventRaw(
convert_gamepad_id(gilrs_event.id),
GamepadEventType::ButtonChanged(button_type, value),
));
}
}
EventType::AxisChanged(gilrs_axis, value, _) => {
if let Some(axis_type) = convert_axis(gilrs_axis) {
event.send(GamepadEvent(
event.send(GamepadEventRaw(
convert_gamepad_id(gilrs_event.id),
GamepadEventType::AxisChanged(axis_type, value),
));
Expand Down
9 changes: 6 additions & 3 deletions crates/bevy_gilrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod gilrs_system;
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use gilrs::GilrsBuilder;
use gilrs_system::{gilrs_event_startup_system, girls_event_system};
use gilrs_system::gilrs_event_system;

#[derive(Default)]
pub struct GilrsPlugin;
Expand All @@ -18,8 +18,11 @@ impl Plugin for GilrsPlugin {
{
Ok(gilrs) => {
app.add_thread_local_resource(gilrs)
.add_startup_system(gilrs_event_startup_system.thread_local_system())
.add_system_to_stage(stage::FIRST, girls_event_system.thread_local_system());
.add_startup_system(gilrs_event_system.thread_local_system())
.add_system_to_stage(
stage::PRE_EVENT,
gilrs_event_system.thread_local_system(),
);
}
Err(err) => log::error!("Failed to start Gilrs. {}", err),
}
Expand Down
131 changes: 82 additions & 49 deletions crates/bevy_input/src/gamepad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub enum GamepadEventType {
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct GamepadEvent(pub Gamepad, pub GamepadEventType);

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct GamepadEventRaw(pub Gamepad, pub GamepadEventType);

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum GamepadButtonType {
Expand Down Expand Up @@ -66,51 +70,51 @@ pub enum GamepadAxisType {
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<GamepadButton, ButtonSetting>,
pub axis_settings: HashMap<GamepadAxis, AxisSetting>,
pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSetting>,
pub struct GamepadSettings {
pub default_button_settings: ButtonSettings,
pub default_axis_settings: AxisSettings,
pub default_button_axis_settings: ButtonAxisSettings,
pub button_settings: HashMap<GamepadButton, ButtonSettings>,
pub axis_settings: HashMap<GamepadAxis, AxisSettings>,
pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSettings>,
}

impl GamepadSetting {
pub fn get_button_setting(&self, button: GamepadButton) -> &ButtonSetting {
impl GamepadSettings {
pub fn get_button_settings(&self, button: GamepadButton) -> &ButtonSettings {
self.button_settings
.get(&button)
.unwrap_or(&self.default_button_setting)
.unwrap_or(&self.default_button_settings)
}

pub fn get_axis_setting(&self, axis: GamepadAxis) -> &AxisSetting {
pub fn get_axis_settings(&self, axis: GamepadAxis) -> &AxisSettings {
self.axis_settings
.get(&axis)
.unwrap_or(&self.default_axis_setting)
.unwrap_or(&self.default_axis_settings)
}

pub fn get_button_axis_setting(&self, button: GamepadButton) -> &ButtonAxisSetting {
pub fn get_button_axis_settings(&self, button: GamepadButton) -> &ButtonAxisSettings {
self.button_axis_settings
.get(&button)
.unwrap_or(&self.default_button_axis_setting)
.unwrap_or(&self.default_button_axis_settings)
}
}

#[derive(Debug, Clone)]
pub struct ButtonSetting {
pub struct ButtonSettings {
pub press: f32,
pub release: f32,
}

impl Default for ButtonSetting {
impl Default for ButtonSettings {
fn default() -> Self {
ButtonSetting {
ButtonSettings {
press: 0.75,
release: 0.65,
}
}
}

impl ButtonSetting {
impl ButtonSettings {
fn is_pressed(&self, value: f32) -> bool {
value >= self.press
}
Expand All @@ -121,17 +125,17 @@ impl ButtonSetting {
}

#[derive(Debug, Clone)]
pub struct AxisSetting {
pub struct AxisSettings {
pub positive_high: f32,
pub positive_low: f32,
pub negative_high: f32,
pub negative_low: f32,
pub threshold: f32,
}

impl Default for AxisSetting {
impl Default for AxisSettings {
fn default() -> Self {
AxisSetting {
AxisSettings {
positive_high: 0.95,
positive_low: 0.05,
negative_high: -0.95,
Expand All @@ -141,7 +145,7 @@ impl Default for AxisSetting {
}
}

impl AxisSetting {
impl AxisSettings {
fn filter(&self, new_value: f32, old_value: Option<f32>) -> f32 {
if let Some(old_value) = old_value {
if (new_value - old_value).abs() <= self.threshold {
Expand All @@ -162,23 +166,23 @@ impl AxisSetting {
}

#[derive(Debug, Clone)]
pub struct ButtonAxisSetting {
pub struct ButtonAxisSettings {
pub high: f32,
pub low: f32,
pub threshold: f32,
}

impl Default for ButtonAxisSetting {
impl Default for ButtonAxisSettings {
fn default() -> Self {
ButtonAxisSetting {
ButtonAxisSettings {
high: 0.95,
low: 0.05,
threshold: 0.01,
}
}
}

impl ButtonAxisSetting {
impl ButtonAxisSettings {
fn filter(&self, new_value: f32, old_value: Option<f32>) -> f32 {
if let Some(old_value) = old_value {
if (new_value - old_value).abs() <= self.threshold {
Expand All @@ -195,65 +199,94 @@ impl ButtonAxisSetting {
}
}

#[derive(Default)]
pub struct GamepadEventState {
gamepad_event_reader: EventReader<GamepadEvent>,
}

#[allow(clippy::float_cmp)]
pub fn gamepad_event_system(
mut state: Local<GamepadEventState>,
mut event_reader: Local<EventReader<GamepadEventRaw>>,
mut button_input: ResMut<Input<GamepadButton>>,
mut axis: ResMut<Axis<GamepadAxis>>,
mut button_axis: ResMut<Axis<GamepadButton>>,
events: Res<Events<GamepadEvent>>,
settings: Res<GamepadSetting>,
raw_events: Res<Events<GamepadEventRaw>>,
mut events: ResMut<Events<GamepadEvent>>,
settings: Res<GamepadSettings>,
) {
button_input.update();
for event in state.gamepad_event_reader.iter(&events) {
let (gamepad, event) = (&event.0, &event.1);
for event in event_reader.iter(&raw_events) {
let (gamepad, event) = (event.0, &event.1);
match event {
GamepadEventType::Connected => {
events.send(GamepadEvent(gamepad, event.clone()));
for button_type in ALL_BUTTON_TYPES.iter() {
let gamepad_button = GamepadButton(*gamepad, *button_type);
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);
axis.set(GamepadAxis(gamepad, *axis_type), 0.0);
}
}
GamepadEventType::Disconnected => {
events.send(GamepadEvent(gamepad, event.clone()));
for button_type in ALL_BUTTON_TYPES.iter() {
let gamepad_button = GamepadButton(*gamepad, *button_type);
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));
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);
let gamepad_axis = GamepadAxis(gamepad, *axis_type);
let old_value = axis.get(gamepad_axis);
let filtered_value = settings
.get_axis_settings(gamepad_axis)
.filter(*value, old_value);
axis.set(gamepad_axis, filtered_value);

// only send event if axis has changed after going through filters
if let Some(old_value) = old_value {
if old_value == filtered_value {
return;
}
} else if filtered_value == 0.0 {
return;
}

events.send(GamepadEvent(
gamepad,
GamepadEventType::AxisChanged(*axis_type, filtered_value),
))
}
GamepadEventType::ButtonChanged(button_type, value) => {
let gamepad_button = GamepadButton(*gamepad, *button_type);
let gamepad_button = GamepadButton(gamepad, *button_type);
let old_value = button_axis.get(gamepad_button);
let filtered_value = settings
.get_button_axis_setting(gamepad_button)
.filter(*value, button_axis.get(gamepad_button));
.get_button_axis_settings(gamepad_button)
.filter(*value, old_value);
button_axis.set(gamepad_button, filtered_value);

let button_property = settings.get_button_setting(gamepad_button);
let button_property = settings.get_button_settings(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);
}

// only send event if axis has changed after going through filters
if let Some(old_value) = old_value {
if old_value == filtered_value {
return;
}
} else if filtered_value == 0.0 {
return;
}

events.send(GamepadEvent(
gamepad,
GamepadEventType::ButtonChanged(*button_type, filtered_value),
))
}
}
}
Expand Down
Loading

0 comments on commit 267599e

Please sign in to comment.