Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gamepad: expose raw and filtered gamepad events. #711

Merged
merged 1 commit into from
Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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