mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
Use Name
component for gamepad (#16233)
# Objective Addressing a suggestion I made in Discord: store gamepad name as a `Name` component. Advantages: - Will be nicely displayed in inspector / editor. - Easier to spawn in tests, just `world.spawn(Gamepad::default())`. ## Solution `Gamepad` component now stores only vendor and product IDs and `Name` stores the gamepad name. Since `GamepadInfo` is no longer necessary, I removed it and merged its fields into the connection event. ## Testing - Run unit tests. --- ## Migration Guide - `GamepadInfo` no longer exists: - Name now accesible via `Name` component. - Other information available on `Gamepad` component directly. - `GamepadConnection::Connected` now stores all info fields directly.
This commit is contained in:
parent
718688e791
commit
282ca735ba
5 changed files with 87 additions and 78 deletions
|
@ -8,7 +8,7 @@ use bevy_ecs::prelude::Commands;
|
||||||
use bevy_ecs::system::NonSendMut;
|
use bevy_ecs::system::NonSendMut;
|
||||||
use bevy_ecs::system::ResMut;
|
use bevy_ecs::system::ResMut;
|
||||||
use bevy_input::gamepad::{
|
use bevy_input::gamepad::{
|
||||||
GamepadConnection, GamepadConnectionEvent, GamepadInfo, RawGamepadAxisChangedEvent,
|
GamepadConnection, GamepadConnectionEvent, RawGamepadAxisChangedEvent,
|
||||||
RawGamepadButtonChangedEvent, RawGamepadEvent,
|
RawGamepadButtonChangedEvent, RawGamepadEvent,
|
||||||
};
|
};
|
||||||
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
|
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
|
||||||
|
@ -26,15 +26,13 @@ pub fn gilrs_event_startup_system(
|
||||||
gamepads.id_to_entity.insert(id, entity);
|
gamepads.id_to_entity.insert(id, entity);
|
||||||
gamepads.entity_to_id.insert(entity, id);
|
gamepads.entity_to_id.insert(entity, id);
|
||||||
|
|
||||||
let info = GamepadInfo {
|
|
||||||
name: gamepad.name().into(),
|
|
||||||
vendor_id: gamepad.vendor_id(),
|
|
||||||
product_id: gamepad.product_id(),
|
|
||||||
};
|
|
||||||
|
|
||||||
events.send(GamepadConnectionEvent {
|
events.send(GamepadConnectionEvent {
|
||||||
gamepad: entity,
|
gamepad: entity,
|
||||||
connection: GamepadConnection::Connected(info),
|
connection: GamepadConnection::Connected {
|
||||||
|
name: gamepad.name().to_string(),
|
||||||
|
vendor_id: gamepad.vendor_id(),
|
||||||
|
product_id: gamepad.product_id(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,20 +60,17 @@ pub fn gilrs_event_system(
|
||||||
entity
|
entity
|
||||||
});
|
});
|
||||||
|
|
||||||
let info = GamepadInfo {
|
let event = GamepadConnectionEvent::new(
|
||||||
name: pad.name().into(),
|
|
||||||
vendor_id: pad.vendor_id(),
|
|
||||||
product_id: pad.product_id(),
|
|
||||||
};
|
|
||||||
|
|
||||||
events.send(
|
|
||||||
GamepadConnectionEvent::new(entity, GamepadConnection::Connected(info.clone()))
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
connection_events.send(GamepadConnectionEvent::new(
|
|
||||||
entity,
|
entity,
|
||||||
GamepadConnection::Connected(info),
|
GamepadConnection::Connected {
|
||||||
));
|
name: pad.name().to_string(),
|
||||||
|
vendor_id: pad.vendor_id(),
|
||||||
|
product_id: pad.product_id(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
events.send(event.clone().into());
|
||||||
|
connection_events.send(event);
|
||||||
}
|
}
|
||||||
EventType::Disconnected => {
|
EventType::Disconnected => {
|
||||||
let gamepad = gamepads
|
let gamepad = gamepads
|
||||||
|
|
|
@ -21,6 +21,7 @@ serialize = ["serde", "smol_str/serde"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# bevy
|
# bevy
|
||||||
bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false }
|
bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false }
|
||||||
|
bevy_core = { path = "../bevy_core", version = "0.15.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false, features = [
|
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false, features = [
|
||||||
"serialize",
|
"serialize",
|
||||||
] }
|
] }
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! The gamepad input functionality.
|
//! The gamepad input functionality.
|
||||||
|
|
||||||
use crate::{Axis, ButtonInput, ButtonState};
|
use crate::{Axis, ButtonInput, ButtonState};
|
||||||
|
use bevy_core::Name;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
change_detection::DetectChangesMut,
|
change_detection::DetectChangesMut,
|
||||||
component::Component,
|
component::Component,
|
||||||
|
@ -148,7 +149,7 @@ impl GamepadConnectionEvent {
|
||||||
|
|
||||||
/// Is the gamepad connected?
|
/// Is the gamepad connected?
|
||||||
pub fn connected(&self) -> bool {
|
pub fn connected(&self) -> bool {
|
||||||
matches!(self.connection, GamepadConnection::Connected(_))
|
matches!(self.connection, GamepadConnection::Connected { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the gamepad disconnected?
|
/// Is the gamepad disconnected?
|
||||||
|
@ -318,10 +319,10 @@ pub enum ButtonSettingsError {
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_input::gamepad::{Gamepad, GamepadAxis, GamepadButton};
|
/// # use bevy_input::gamepad::{Gamepad, GamepadAxis, GamepadButton};
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::system::Query;
|
||||||
|
/// # use bevy_core::Name;
|
||||||
/// #
|
/// #
|
||||||
/// fn gamepad_usage_system(gamepads: Query<&Gamepad>) {
|
/// fn gamepad_usage_system(gamepads: Query<(&Name, &Gamepad)>) {
|
||||||
/// for gamepad in &gamepads {
|
/// for (name, gamepad) in &gamepads {
|
||||||
/// let name = &gamepad.info.name;
|
|
||||||
/// println!("{name}");
|
/// println!("{name}");
|
||||||
///
|
///
|
||||||
/// if gamepad.digital.just_pressed(GamepadButton::North) {
|
/// if gamepad.digital.just_pressed(GamepadButton::North) {
|
||||||
|
@ -338,31 +339,22 @@ pub enum ButtonSettingsError {
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
|
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
|
||||||
#[require(GamepadSettings)]
|
#[require(GamepadSettings)]
|
||||||
pub struct Gamepad {
|
pub struct Gamepad {
|
||||||
/// Metadata.
|
/// The USB vendor ID as assigned by the USB-IF, if available.
|
||||||
pub info: GamepadInfo,
|
pub vendor_id: Option<u16>,
|
||||||
|
|
||||||
|
/// The USB product ID as assigned by the [vendor], if available.
|
||||||
|
///
|
||||||
|
/// [vendor]: Self::vendor_id
|
||||||
|
pub product_id: Option<u16>,
|
||||||
|
|
||||||
/// [`ButtonInput`] of [`GamepadButton`] representing their digital state
|
/// [`ButtonInput`] of [`GamepadButton`] representing their digital state
|
||||||
pub digital: ButtonInput<GamepadButton>,
|
pub digital: ButtonInput<GamepadButton>,
|
||||||
|
|
||||||
/// [`Axis`] of [`GamepadButton`] representing their analog state.
|
/// [`Axis`] of [`GamepadButton`] representing their analog state.
|
||||||
pub analog: Axis<GamepadInput>,
|
pub analog: Axis<GamepadInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gamepad {
|
impl Gamepad {
|
||||||
/// Creates a gamepad with the given metadata.
|
|
||||||
pub fn new(info: GamepadInfo) -> Self {
|
|
||||||
let mut analog = Axis::default();
|
|
||||||
for button in GamepadButton::all().iter().copied() {
|
|
||||||
analog.set(button, 0.0);
|
|
||||||
}
|
|
||||||
for axis_type in GamepadAxis::all().iter().copied() {
|
|
||||||
analog.set(axis_type, 0.0);
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
info,
|
|
||||||
analog,
|
|
||||||
digital: ButtonInput::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the left stick as a [`Vec2`]
|
/// Returns the left stick as a [`Vec2`]
|
||||||
pub fn left_stick(&self) -> Vec2 {
|
pub fn left_stick(&self) -> Vec2 {
|
||||||
Vec2 {
|
Vec2 {
|
||||||
|
@ -390,32 +382,23 @@ impl Gamepad {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that we don't expose `gilrs::Gamepad::uuid` due to
|
impl Default for Gamepad {
|
||||||
// https://gitlab.com/gilrs-project/gilrs/-/issues/153.
|
fn default() -> Self {
|
||||||
//
|
let mut analog = Axis::default();
|
||||||
/// Metadata associated with a [`Gamepad`].
|
for button in GamepadButton::all().iter().copied() {
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
analog.set(button, 0.0);
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
|
}
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
for axis_type in GamepadAxis::all().iter().copied() {
|
||||||
#[cfg_attr(
|
analog.set(axis_type, 0.0);
|
||||||
all(feature = "serialize", feature = "bevy_reflect"),
|
}
|
||||||
reflect(Serialize, Deserialize)
|
|
||||||
)]
|
|
||||||
pub struct GamepadInfo {
|
|
||||||
/// The name of the gamepad.
|
|
||||||
///
|
|
||||||
/// This name is generally defined by the OS.
|
|
||||||
///
|
|
||||||
/// For example on Windows the name may be "HID-compliant game controller".
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
/// The USB vendor ID as assigned by the USB-IF, if available.
|
Self {
|
||||||
pub vendor_id: Option<u16>,
|
vendor_id: None,
|
||||||
|
product_id: None,
|
||||||
/// The USB product ID as assigned by the [vendor], if available.
|
digital: Default::default(),
|
||||||
///
|
analog,
|
||||||
/// [vendor]: Self::vendor_id
|
}
|
||||||
pub product_id: Option<u16>,
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents gamepad input types that are mapped in the range [0.0, 1.0].
|
/// Represents gamepad input types that are mapped in the range [0.0, 1.0].
|
||||||
|
@ -1227,12 +1210,23 @@ pub fn gamepad_connection_system(
|
||||||
for connection_event in connection_events.read() {
|
for connection_event in connection_events.read() {
|
||||||
let id = connection_event.gamepad;
|
let id = connection_event.gamepad;
|
||||||
match &connection_event.connection {
|
match &connection_event.connection {
|
||||||
GamepadConnection::Connected(info) => {
|
GamepadConnection::Connected {
|
||||||
|
name,
|
||||||
|
vendor_id,
|
||||||
|
product_id,
|
||||||
|
} => {
|
||||||
let Some(mut gamepad) = commands.get_entity(id) else {
|
let Some(mut gamepad) = commands.get_entity(id) else {
|
||||||
warn!("Gamepad {:} removed before handling connection event.", id);
|
warn!("Gamepad {:} removed before handling connection event.", id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
gamepad.insert(Gamepad::new(info.clone()));
|
gamepad.insert((
|
||||||
|
Name::new(name.clone()),
|
||||||
|
Gamepad {
|
||||||
|
vendor_id: *vendor_id,
|
||||||
|
product_id: *product_id,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
));
|
||||||
info!("Gamepad {:?} connected.", id);
|
info!("Gamepad {:?} connected.", id);
|
||||||
}
|
}
|
||||||
GamepadConnection::Disconnected => {
|
GamepadConnection::Disconnected => {
|
||||||
|
@ -1250,6 +1244,9 @@ pub fn gamepad_connection_system(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that we don't expose `gilrs::Gamepad::uuid` due to
|
||||||
|
// https://gitlab.com/gilrs-project/gilrs/-/issues/153.
|
||||||
|
//
|
||||||
/// The connection status of a gamepad.
|
/// The connection status of a gamepad.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
|
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
|
||||||
|
@ -1260,7 +1257,20 @@ pub fn gamepad_connection_system(
|
||||||
)]
|
)]
|
||||||
pub enum GamepadConnection {
|
pub enum GamepadConnection {
|
||||||
/// The gamepad is connected.
|
/// The gamepad is connected.
|
||||||
Connected(GamepadInfo),
|
Connected {
|
||||||
|
/// The name of the gamepad.
|
||||||
|
///
|
||||||
|
/// This name is generally defined by the OS.
|
||||||
|
///
|
||||||
|
/// For example on Windows the name may be "HID-compliant game controller".
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
/// The USB vendor ID as assigned by the USB-IF, if available.
|
||||||
|
vendor_id: Option<u16>,
|
||||||
|
|
||||||
|
/// The USB product ID as assigned by the vendor, if available.
|
||||||
|
product_id: Option<u16>,
|
||||||
|
},
|
||||||
/// The gamepad is disconnected.
|
/// The gamepad is disconnected.
|
||||||
Disconnected,
|
Disconnected,
|
||||||
}
|
}
|
||||||
|
@ -1497,8 +1507,8 @@ mod tests {
|
||||||
GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
|
GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
|
||||||
GamepadButtonStateChangedEvent,
|
GamepadButtonStateChangedEvent,
|
||||||
GamepadConnection::{Connected, Disconnected},
|
GamepadConnection::{Connected, Disconnected},
|
||||||
GamepadConnectionEvent, GamepadEvent, GamepadInfo, GamepadSettings,
|
GamepadConnectionEvent, GamepadEvent, GamepadSettings, RawGamepadAxisChangedEvent,
|
||||||
RawGamepadAxisChangedEvent, RawGamepadButtonChangedEvent, RawGamepadEvent,
|
RawGamepadButtonChangedEvent, RawGamepadEvent,
|
||||||
};
|
};
|
||||||
use crate::ButtonState;
|
use crate::ButtonState;
|
||||||
use bevy_app::{App, PreUpdate};
|
use bevy_app::{App, PreUpdate};
|
||||||
|
@ -1871,7 +1881,11 @@ mod tests {
|
||||||
.resource_mut::<Events<GamepadConnectionEvent>>()
|
.resource_mut::<Events<GamepadConnectionEvent>>()
|
||||||
.send(GamepadConnectionEvent::new(
|
.send(GamepadConnectionEvent::new(
|
||||||
gamepad,
|
gamepad,
|
||||||
Connected(GamepadInfo::default()),
|
Connected {
|
||||||
|
name: "Test gamepad".to_string(),
|
||||||
|
vendor_id: None,
|
||||||
|
product_id: None,
|
||||||
|
},
|
||||||
));
|
));
|
||||||
gamepad
|
gamepad
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ use gamepad::{
|
||||||
gamepad_connection_system, gamepad_event_processing_system, GamepadAxis,
|
gamepad_connection_system, gamepad_event_processing_system, GamepadAxis,
|
||||||
GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
|
GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
|
||||||
GamepadButtonStateChangedEvent, GamepadConnection, GamepadConnectionEvent, GamepadEvent,
|
GamepadButtonStateChangedEvent, GamepadConnection, GamepadConnectionEvent, GamepadEvent,
|
||||||
GamepadInfo, GamepadInput, GamepadRumbleRequest, GamepadSettings, RawGamepadAxisChangedEvent,
|
GamepadInput, GamepadRumbleRequest, GamepadSettings, RawGamepadAxisChangedEvent,
|
||||||
RawGamepadButtonChangedEvent, RawGamepadEvent,
|
RawGamepadButtonChangedEvent, RawGamepadEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,7 +142,6 @@ impl Plugin for InputPlugin {
|
||||||
.register_type::<GamepadButtonChangedEvent>()
|
.register_type::<GamepadButtonChangedEvent>()
|
||||||
.register_type::<GamepadAxisChangedEvent>()
|
.register_type::<GamepadAxisChangedEvent>()
|
||||||
.register_type::<GamepadButtonStateChangedEvent>()
|
.register_type::<GamepadButtonStateChangedEvent>()
|
||||||
.register_type::<GamepadInfo>()
|
|
||||||
.register_type::<GamepadConnection>()
|
.register_type::<GamepadConnection>()
|
||||||
.register_type::<GamepadSettings>()
|
.register_type::<GamepadSettings>()
|
||||||
.register_type::<GamepadAxis>()
|
.register_type::<GamepadAxis>()
|
||||||
|
|
|
@ -447,7 +447,7 @@ fn update_axes(
|
||||||
|
|
||||||
fn update_connected(
|
fn update_connected(
|
||||||
mut connected: EventReader<GamepadConnectionEvent>,
|
mut connected: EventReader<GamepadConnectionEvent>,
|
||||||
gamepads: Query<(Entity, &Gamepad)>,
|
gamepads: Query<(Entity, &Name), With<Gamepad>>,
|
||||||
text: Single<Entity, With<ConnectedGamepadsText>>,
|
text: Single<Entity, With<ConnectedGamepadsText>>,
|
||||||
mut writer: TextUiWriter,
|
mut writer: TextUiWriter,
|
||||||
) {
|
) {
|
||||||
|
@ -458,7 +458,7 @@ fn update_connected(
|
||||||
|
|
||||||
let formatted = gamepads
|
let formatted = gamepads
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(entity, gamepad)| format!("{} - {}", entity, gamepad.info.name))
|
.map(|(entity, name)| format!("{} - {}", entity, name))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue