mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
feat: add GamepadInfo, expose gamepad names (#6342)
# Objective Fixes #6339. ## Solution This PR adds a new type, `GamepadInfo`, which holds metadata associated with a particular `Gamepad`. The `Gamepads` resource now holds a `HashMap<Gamepad, GamepadInfo>`. The `GamepadInfo` is created when the gamepad backend (by default `bevy_gilrs`) emits a "gamepad connected" event. The `gamepad_viewer` example has been updated to showcase the new functionality. Before: ![bevy-gamepad-old](https://user-images.githubusercontent.com/86984145/197359427-2130a3c0-bd8a-4683-ae24-2a9eaa98b586.png) After: ![bevy-gamepad-new](https://user-images.githubusercontent.com/86984145/197359429-f7963163-df26-4906-af7f-6186fe3bd338.png) --- ## Changelog ### Added - Added `GamepadInfo`. - Added `Gamepads::name()`, which returns the name of the specified gamepad if it exists. ### Changed - `GamepadEventType::Connected` is now a tuple variant with a single field of type `GamepadInfo`. - Since `GamepadInfo` is not `Copy`, `GamepadEventType` is no longer `Copy`. The same is true of `GamepadEvent` and `GamepadEventRaw`. ## Migration Guide - Pattern matches on `GamepadEventType::Connected` will need to be updated, as the form of the variant has changed. - Code that requires `GamepadEvent`, `GamepadEventRaw` or `GamepadEventType` to be `Copy` will need to be updated.
This commit is contained in:
parent
c9ec5c771a
commit
bcc33f6757
4 changed files with 56 additions and 33 deletions
|
@ -1,14 +1,19 @@
|
|||
use crate::converter::{convert_axis, convert_button, convert_gamepad_id};
|
||||
use bevy_ecs::event::EventWriter;
|
||||
use bevy_ecs::system::{NonSend, NonSendMut};
|
||||
use bevy_input::gamepad::GamepadInfo;
|
||||
use bevy_input::{gamepad::GamepadEventRaw, prelude::*};
|
||||
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter, Gilrs};
|
||||
|
||||
pub fn gilrs_event_startup_system(gilrs: NonSend<Gilrs>, mut events: EventWriter<GamepadEventRaw>) {
|
||||
for (id, _) in gilrs.gamepads() {
|
||||
for (id, gamepad) in gilrs.gamepads() {
|
||||
let info = GamepadInfo {
|
||||
name: gamepad.name().into(),
|
||||
};
|
||||
|
||||
events.send(GamepadEventRaw::new(
|
||||
convert_gamepad_id(id),
|
||||
GamepadEventType::Connected,
|
||||
GamepadEventType::Connected(info),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +27,14 @@ pub fn gilrs_event_system(mut gilrs: NonSendMut<Gilrs>, mut events: EventWriter<
|
|||
|
||||
match gilrs_event.event {
|
||||
EventType::Connected => {
|
||||
let pad = gilrs.gamepad(gilrs_event.id);
|
||||
let info = GamepadInfo {
|
||||
name: pad.name().into(),
|
||||
};
|
||||
|
||||
events.send(GamepadEventRaw::new(
|
||||
convert_gamepad_id(gilrs_event.id),
|
||||
GamepadEventType::Connected,
|
||||
GamepadEventType::Connected(info),
|
||||
));
|
||||
}
|
||||
EventType::Disconnected => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{Axis, Input};
|
||||
use bevy_ecs::event::{EventReader, EventWriter};
|
||||
use bevy_ecs::system::{Res, ResMut, Resource};
|
||||
use bevy_utils::{tracing::info, HashMap, HashSet};
|
||||
use bevy_utils::{tracing::info, HashMap};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that occur when setting axis settings for gamepad input.
|
||||
|
@ -78,6 +78,13 @@ impl Gamepad {
|
|||
}
|
||||
}
|
||||
|
||||
/// Metadata associated with a `Gamepad`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GamepadInfo {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// A collection of connected [`Gamepad`]s.
|
||||
///
|
||||
/// ## Usage
|
||||
|
@ -92,23 +99,27 @@ impl Gamepad {
|
|||
#[derive(Resource, Default, Debug)]
|
||||
pub struct Gamepads {
|
||||
/// The collection of the connected [`Gamepad`]s.
|
||||
gamepads: HashSet<Gamepad>,
|
||||
gamepads: HashMap<Gamepad, GamepadInfo>,
|
||||
}
|
||||
|
||||
impl Gamepads {
|
||||
/// Returns `true` if the `gamepad` is connected.
|
||||
pub fn contains(&self, gamepad: Gamepad) -> bool {
|
||||
self.gamepads.contains(&gamepad)
|
||||
self.gamepads.contains_key(&gamepad)
|
||||
}
|
||||
|
||||
/// Returns an iterator over registered [`Gamepad`]s in an arbitrary order.
|
||||
pub fn iter(&self) -> impl Iterator<Item = Gamepad> + '_ {
|
||||
self.gamepads.iter().copied()
|
||||
self.gamepads.keys().copied()
|
||||
}
|
||||
|
||||
pub fn name(&self, gamepad: Gamepad) -> Option<&str> {
|
||||
self.gamepads.get(&gamepad).map(|g| g.name.as_str())
|
||||
}
|
||||
|
||||
/// Registers the `gamepad`, marking it as connected.
|
||||
fn register(&mut self, gamepad: Gamepad) {
|
||||
self.gamepads.insert(gamepad);
|
||||
fn register(&mut self, gamepad: Gamepad, info: GamepadInfo) {
|
||||
self.gamepads.insert(gamepad, info);
|
||||
}
|
||||
|
||||
/// Deregisters the `gamepad`, marking it as disconnected.
|
||||
|
@ -118,11 +129,11 @@ impl Gamepads {
|
|||
}
|
||||
|
||||
/// The data contained in a [`GamepadEvent`] or [`GamepadEventRaw`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum GamepadEventType {
|
||||
/// A [`Gamepad`] has been connected.
|
||||
Connected,
|
||||
Connected(GamepadInfo),
|
||||
/// A [`Gamepad`] has been disconnected.
|
||||
Disconnected,
|
||||
|
||||
|
@ -151,7 +162,7 @@ pub enum GamepadEventType {
|
|||
/// [`Axis<GamepadAxis>`], and [`Axis<GamepadButton>`] resources won't be updated correctly.
|
||||
///
|
||||
/// An example for gamepad input mocking can be seen in the documentation of the [`GamepadEventRaw`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GamepadEvent {
|
||||
/// The gamepad this event corresponds to.
|
||||
|
@ -191,7 +202,7 @@ impl GamepadEvent {
|
|||
/// ```
|
||||
/// # use bevy_input::prelude::*;
|
||||
/// # use bevy_input::InputPlugin;
|
||||
/// # use bevy_input::gamepad::GamepadEventRaw;
|
||||
/// # use bevy_input::gamepad::{GamepadEventRaw, GamepadInfo};
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #[derive(Resource)]
|
||||
|
@ -223,7 +234,8 @@ impl GamepadEvent {
|
|||
///
|
||||
/// // Send the gamepad connected event to mark our gamepad as connected.
|
||||
/// // This updates the `Gamepads` resource accordingly.
|
||||
/// app.world.send_event(GamepadEventRaw::new(gamepad, GamepadEventType::Connected));
|
||||
/// let info = GamepadInfo { name: "Mock Gamepad".into() };
|
||||
/// app.world.send_event(GamepadEventRaw::new(gamepad, GamepadEventType::Connected(info)));
|
||||
///
|
||||
/// // Send the gamepad input event to mark the `South` gamepad button as pressed.
|
||||
/// // This updates the `Input<GamepadButton>` resource accordingly.
|
||||
|
@ -254,7 +266,7 @@ impl GamepadEvent {
|
|||
/// #
|
||||
/// # bevy_ecs::system::assert_is_system(change_resource_on_gamepad_button_press);
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GamepadEventRaw {
|
||||
/// The gamepad this event corresponds to.
|
||||
|
@ -1062,11 +1074,12 @@ pub fn gamepad_connection_system(
|
|||
mut gamepad_event: EventReader<GamepadEvent>,
|
||||
) {
|
||||
for event in gamepad_event.iter() {
|
||||
match event.event_type {
|
||||
GamepadEventType::Connected => {
|
||||
gamepads.register(event.gamepad);
|
||||
match &event.event_type {
|
||||
GamepadEventType::Connected(info) => {
|
||||
gamepads.register(event.gamepad, info.clone());
|
||||
info!("{:?} Connected", event.gamepad);
|
||||
}
|
||||
|
||||
GamepadEventType::Disconnected => {
|
||||
gamepads.deregister(event.gamepad);
|
||||
info!("{:?} Disconnected", event.gamepad);
|
||||
|
@ -1096,9 +1109,9 @@ pub fn gamepad_event_system(
|
|||
) {
|
||||
button_input.clear();
|
||||
for event in raw_events.iter() {
|
||||
match event.event_type {
|
||||
GamepadEventType::Connected => {
|
||||
events.send(GamepadEvent::new(event.gamepad, event.event_type));
|
||||
match &event.event_type {
|
||||
GamepadEventType::Connected(_) => {
|
||||
events.send(GamepadEvent::new(event.gamepad, event.event_type.clone()));
|
||||
for button_type in &ALL_BUTTON_TYPES {
|
||||
let gamepad_button = GamepadButton::new(event.gamepad, *button_type);
|
||||
button_input.reset(gamepad_button);
|
||||
|
@ -1109,7 +1122,7 @@ pub fn gamepad_event_system(
|
|||
}
|
||||
}
|
||||
GamepadEventType::Disconnected => {
|
||||
events.send(GamepadEvent::new(event.gamepad, event.event_type));
|
||||
events.send(GamepadEvent::new(event.gamepad, event.event_type.clone()));
|
||||
for button_type in &ALL_BUTTON_TYPES {
|
||||
let gamepad_button = GamepadButton::new(event.gamepad, *button_type);
|
||||
button_input.reset(gamepad_button);
|
||||
|
@ -1120,37 +1133,37 @@ pub fn gamepad_event_system(
|
|||
}
|
||||
}
|
||||
GamepadEventType::AxisChanged(axis_type, value) => {
|
||||
let gamepad_axis = GamepadAxis::new(event.gamepad, axis_type);
|
||||
let gamepad_axis = GamepadAxis::new(event.gamepad, *axis_type);
|
||||
if let Some(filtered_value) = settings
|
||||
.get_axis_settings(gamepad_axis)
|
||||
.filter(value, axis.get(gamepad_axis))
|
||||
.filter(*value, axis.get(gamepad_axis))
|
||||
{
|
||||
axis.set(gamepad_axis, filtered_value);
|
||||
events.send(GamepadEvent::new(
|
||||
event.gamepad,
|
||||
GamepadEventType::AxisChanged(axis_type, filtered_value),
|
||||
GamepadEventType::AxisChanged(*axis_type, filtered_value),
|
||||
));
|
||||
}
|
||||
}
|
||||
GamepadEventType::ButtonChanged(button_type, value) => {
|
||||
let gamepad_button = GamepadButton::new(event.gamepad, button_type);
|
||||
let gamepad_button = GamepadButton::new(event.gamepad, *button_type);
|
||||
if let Some(filtered_value) = settings
|
||||
.get_button_axis_settings(gamepad_button)
|
||||
.filter(value, button_axis.get(gamepad_button))
|
||||
.filter(*value, button_axis.get(gamepad_button))
|
||||
{
|
||||
button_axis.set(gamepad_button, filtered_value);
|
||||
events.send(GamepadEvent::new(
|
||||
event.gamepad,
|
||||
GamepadEventType::ButtonChanged(button_type, filtered_value),
|
||||
GamepadEventType::ButtonChanged(*button_type, filtered_value),
|
||||
));
|
||||
}
|
||||
|
||||
let button_property = settings.get_button_settings(gamepad_button);
|
||||
if button_input.pressed(gamepad_button) {
|
||||
if button_property.is_released(value) {
|
||||
if button_property.is_released(*value) {
|
||||
button_input.release(gamepad_button);
|
||||
}
|
||||
} else if button_property.is_pressed(value) {
|
||||
} else if button_property.is_pressed(*value) {
|
||||
button_input.press(gamepad_button);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ fn main() {
|
|||
fn gamepad_events(mut gamepad_event: EventReader<GamepadEvent>) {
|
||||
for event in gamepad_event.iter() {
|
||||
match event.event_type {
|
||||
GamepadEventType::Connected => {
|
||||
GamepadEventType::Connected(_) => {
|
||||
info!("{:?} Connected", event.gamepad);
|
||||
}
|
||||
GamepadEventType::Disconnected => {
|
||||
|
|
|
@ -437,7 +437,7 @@ fn setup_connected(mut commands: Commands, font: Res<FontHandle>) {
|
|||
commands.spawn((
|
||||
TextBundle::from_sections([
|
||||
TextSection {
|
||||
value: "Connected Gamepads\n".to_string(),
|
||||
value: "Connected Gamepads:\n".to_string(),
|
||||
style: style.clone(),
|
||||
},
|
||||
TextSection {
|
||||
|
@ -521,7 +521,7 @@ fn update_connected(
|
|||
|
||||
let formatted = gamepads
|
||||
.iter()
|
||||
.map(|g| format!("{:?}", g))
|
||||
.map(|g| format!("- {}", gamepads.name(g).unwrap()))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
|
|
Loading…
Reference in a new issue