Separate gamepad state code from gamepad event code and other customizations (#700)

Separated gamepad event and gamepad state code and made gamepad input more customizable
This commit is contained in:
Utkarsh 2020-10-21 22:57:00 +05:30 committed by GitHub
parent 0f43fb066f
commit d01ba9e4fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 354 additions and 232 deletions

View file

@ -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"

View file

@ -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::<Gilrs>().unwrap();
let mut gamepad_event = resources.get_mut::<Events<GamepadEvent>>().unwrap();
let mut inputs = resources.get_mut::<Input<GamepadButton>>().unwrap();
let mut axes = resources.get_mut::<Axis<GamepadAxis>>().unwrap();
let mut button_axes = resources.get_mut::<Axis<GamepadButton>>().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::<Events<GamepadEvent>>().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::<Gilrs>().unwrap();
let mut gamepad_event = resources.get_mut::<Events<GamepadEvent>>().unwrap();
let mut inputs = resources.get_mut::<Input<GamepadButton>>().unwrap();
let mut axes = resources.get_mut::<Axis<GamepadAxis>>().unwrap();
let mut button_axes = resources.get_mut::<Axis<GamepadButton>>().unwrap();
gamepad_event.update();
inputs.update();
let mut event = resources.get_mut::<Events<GamepadEvent>>().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,
);
}
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, _) => {
if let Some(button_type) = convert_button(gilrs_button) {
inputs.release(GamepadButton(
convert_gamepad_id(gilrs_event.id),
button_type,
));
}
GamepadEventType::Disconnected,
));
}
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,
);
event.send(GamepadEvent(
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) {
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<GamepadEvent>,
inputs: &mut Input<GamepadButton>,
axes: &mut Axis<GamepadAxis>,
button_axes: &mut Axis<GamepadButton>,
) {
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<GamepadEvent>,
inputs: &mut Input<GamepadButton>,
axes: &mut Axis<GamepadAxis>,
button_axes: &mut Axis<GamepadButton>,
) {
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));
}

View file

@ -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),
}

View file

@ -24,11 +24,11 @@ where
self.axis_data.insert(axis, value)
}
pub fn get(&self, axis: &T) -> Option<f32> {
self.axis_data.get(axis).copied()
pub fn get(&self, axis: T) -> Option<f32> {
self.axis_data.get(&axis).copied()
}
pub fn remove(&mut self, axis: &T) -> Option<f32> {
self.axis_data.remove(axis)
pub fn remove(&mut self, axis: T) -> Option<f32> {
self.axis_data.remove(&axis)
}
}

View file

@ -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<GamepadButton, ButtonSetting>,
pub axis_settings: HashMap<GamepadAxis, AxisSetting>,
pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSetting>,
}
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>) -> 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>) -> 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<GamepadEvent>,
}
pub fn gamepad_event_system(
mut state: Local<GamepadEventState>,
mut button_input: ResMut<Input<GamepadButton>>,
mut axis: ResMut<Axis<GamepadAxis>>,
mut button_axis: ResMut<Axis<GamepadButton>>,
events: Res<Events<GamepadEvent>>,
settings: Res<GamepadSetting>,
) {
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,
];

View file

@ -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::<GamepadEvent>()
.init_resource::<GamepadSetting>()
.init_resource::<Input<GamepadButton>>()
.init_resource::<Axis<GamepadAxis>>()
.init_resource::<Axis<GamepadButton>>()
.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::<TouchInput>()
.init_resource::<Touches>()
.add_system_to_stage(

View file

@ -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::<GamepadLobby>()
.add_startup_system(connection_system.system())
.add_system(connection_system.system())
.add_system(button_system.system())
.add_system(axis_system.system())
.init_resource::<GamepadLobby>()
.add_system(gamepad_system.system())
.run();
}
@ -24,85 +23,49 @@ fn connection_system(mut lobby: ResMut<GamepadLobby>, gamepad_event: Res<Events<
match &event {
GamepadEvent(gamepad, GamepadEventType::Connected) => {
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<GamepadLobby>,
inputs: Res<Input<GamepadButton>>,
button_inputs: Res<Input<GamepadButton>>,
button_axes: Res<Axis<GamepadButton>>,
axes: Res<Axis<GamepadAxis>>,
) {
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
);
}
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 axis_system(lobby: Res<GamepadLobby>, axes: Res<Axis<GamepadAxis>>) {
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);
}
}
}
}
}
fn value_check(value: f32) -> bool {
let value = value.abs();
value > 0.1f32 && value < 0.9f32
}

View file

@ -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<GamepadEvent>,
}
fn gamepad_raw_events(mut state: Local<State>, gamepad_event: Res<Events<GamepadEvent>>) {
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);
}
}
}
}