Skip to content

Commit 282ca73

Browse files
authored
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.
1 parent 718688e commit 282ca73

File tree

5 files changed

+87
-78
lines changed

5 files changed

+87
-78
lines changed

crates/bevy_gilrs/src/gilrs_system.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bevy_ecs::prelude::Commands;
88
use bevy_ecs::system::NonSendMut;
99
use bevy_ecs::system::ResMut;
1010
use bevy_input::gamepad::{
11-
GamepadConnection, GamepadConnectionEvent, GamepadInfo, RawGamepadAxisChangedEvent,
11+
GamepadConnection, GamepadConnectionEvent, RawGamepadAxisChangedEvent,
1212
RawGamepadButtonChangedEvent, RawGamepadEvent,
1313
};
1414
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
@@ -26,15 +26,13 @@ pub fn gilrs_event_startup_system(
2626
gamepads.id_to_entity.insert(id, entity);
2727
gamepads.entity_to_id.insert(entity, id);
2828

29-
let info = GamepadInfo {
30-
name: gamepad.name().into(),
31-
vendor_id: gamepad.vendor_id(),
32-
product_id: gamepad.product_id(),
33-
};
34-
3529
events.send(GamepadConnectionEvent {
3630
gamepad: entity,
37-
connection: GamepadConnection::Connected(info),
31+
connection: GamepadConnection::Connected {
32+
name: gamepad.name().to_string(),
33+
vendor_id: gamepad.vendor_id(),
34+
product_id: gamepad.product_id(),
35+
},
3836
});
3937
}
4038
}
@@ -62,20 +60,17 @@ pub fn gilrs_event_system(
6260
entity
6361
});
6462

65-
let info = GamepadInfo {
66-
name: pad.name().into(),
67-
vendor_id: pad.vendor_id(),
68-
product_id: pad.product_id(),
69-
};
70-
71-
events.send(
72-
GamepadConnectionEvent::new(entity, GamepadConnection::Connected(info.clone()))
73-
.into(),
74-
);
75-
connection_events.send(GamepadConnectionEvent::new(
63+
let event = GamepadConnectionEvent::new(
7664
entity,
77-
GamepadConnection::Connected(info),
78-
));
65+
GamepadConnection::Connected {
66+
name: pad.name().to_string(),
67+
vendor_id: pad.vendor_id(),
68+
product_id: pad.product_id(),
69+
},
70+
);
71+
72+
events.send(event.clone().into());
73+
connection_events.send(event);
7974
}
8075
EventType::Disconnected => {
8176
let gamepad = gamepads

crates/bevy_input/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ serialize = ["serde", "smol_str/serde"]
2121
[dependencies]
2222
# bevy
2323
bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false }
24+
bevy_core = { path = "../bevy_core", version = "0.15.0-dev" }
2425
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false, features = [
2526
"serialize",
2627
] }

crates/bevy_input/src/gamepad.rs

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! The gamepad input functionality.
22
33
use crate::{Axis, ButtonInput, ButtonState};
4+
use bevy_core::Name;
45
use bevy_ecs::{
56
change_detection::DetectChangesMut,
67
component::Component,
@@ -148,7 +149,7 @@ impl GamepadConnectionEvent {
148149

149150
/// Is the gamepad connected?
150151
pub fn connected(&self) -> bool {
151-
matches!(self.connection, GamepadConnection::Connected(_))
152+
matches!(self.connection, GamepadConnection::Connected { .. })
152153
}
153154

154155
/// Is the gamepad disconnected?
@@ -318,10 +319,10 @@ pub enum ButtonSettingsError {
318319
/// ```
319320
/// # use bevy_input::gamepad::{Gamepad, GamepadAxis, GamepadButton};
320321
/// # use bevy_ecs::system::Query;
322+
/// # use bevy_core::Name;
321323
/// #
322-
/// fn gamepad_usage_system(gamepads: Query<&Gamepad>) {
323-
/// for gamepad in &gamepads {
324-
/// let name = &gamepad.info.name;
324+
/// fn gamepad_usage_system(gamepads: Query<(&Name, &Gamepad)>) {
325+
/// for (name, gamepad) in &gamepads {
325326
/// println!("{name}");
326327
///
327328
/// if gamepad.digital.just_pressed(GamepadButton::North) {
@@ -338,31 +339,22 @@ pub enum ButtonSettingsError {
338339
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
339340
#[require(GamepadSettings)]
340341
pub struct Gamepad {
341-
/// Metadata.
342-
pub info: GamepadInfo,
342+
/// The USB vendor ID as assigned by the USB-IF, if available.
343+
pub vendor_id: Option<u16>,
344+
345+
/// The USB product ID as assigned by the [vendor], if available.
346+
///
347+
/// [vendor]: Self::vendor_id
348+
pub product_id: Option<u16>,
349+
343350
/// [`ButtonInput`] of [`GamepadButton`] representing their digital state
344351
pub digital: ButtonInput<GamepadButton>,
352+
345353
/// [`Axis`] of [`GamepadButton`] representing their analog state.
346354
pub analog: Axis<GamepadInput>,
347355
}
348356

349357
impl Gamepad {
350-
/// Creates a gamepad with the given metadata.
351-
pub fn new(info: GamepadInfo) -> Self {
352-
let mut analog = Axis::default();
353-
for button in GamepadButton::all().iter().copied() {
354-
analog.set(button, 0.0);
355-
}
356-
for axis_type in GamepadAxis::all().iter().copied() {
357-
analog.set(axis_type, 0.0);
358-
}
359-
Self {
360-
info,
361-
analog,
362-
digital: ButtonInput::default(),
363-
}
364-
}
365-
366358
/// Returns the left stick as a [`Vec2`]
367359
pub fn left_stick(&self) -> Vec2 {
368360
Vec2 {
@@ -390,32 +382,23 @@ impl Gamepad {
390382
}
391383
}
392384

393-
// Note that we don't expose `gilrs::Gamepad::uuid` due to
394-
// https://gitlab.com/gilrs-project/gilrs/-/issues/153.
395-
//
396-
/// Metadata associated with a [`Gamepad`].
397-
#[derive(Debug, Default, Clone, PartialEq, Eq)]
398-
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
399-
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
400-
#[cfg_attr(
401-
all(feature = "serialize", feature = "bevy_reflect"),
402-
reflect(Serialize, Deserialize)
403-
)]
404-
pub struct GamepadInfo {
405-
/// The name of the gamepad.
406-
///
407-
/// This name is generally defined by the OS.
408-
///
409-
/// For example on Windows the name may be "HID-compliant game controller".
410-
pub name: String,
411-
412-
/// The USB vendor ID as assigned by the USB-IF, if available.
413-
pub vendor_id: Option<u16>,
385+
impl Default for Gamepad {
386+
fn default() -> Self {
387+
let mut analog = Axis::default();
388+
for button in GamepadButton::all().iter().copied() {
389+
analog.set(button, 0.0);
390+
}
391+
for axis_type in GamepadAxis::all().iter().copied() {
392+
analog.set(axis_type, 0.0);
393+
}
414394

415-
/// The USB product ID as assigned by the [vendor], if available.
416-
///
417-
/// [vendor]: Self::vendor_id
418-
pub product_id: Option<u16>,
395+
Self {
396+
vendor_id: None,
397+
product_id: None,
398+
digital: Default::default(),
399+
analog,
400+
}
401+
}
419402
}
420403

421404
/// Represents gamepad input types that are mapped in the range [0.0, 1.0].
@@ -1227,12 +1210,23 @@ pub fn gamepad_connection_system(
12271210
for connection_event in connection_events.read() {
12281211
let id = connection_event.gamepad;
12291212
match &connection_event.connection {
1230-
GamepadConnection::Connected(info) => {
1213+
GamepadConnection::Connected {
1214+
name,
1215+
vendor_id,
1216+
product_id,
1217+
} => {
12311218
let Some(mut gamepad) = commands.get_entity(id) else {
12321219
warn!("Gamepad {:} removed before handling connection event.", id);
12331220
continue;
12341221
};
1235-
gamepad.insert(Gamepad::new(info.clone()));
1222+
gamepad.insert((
1223+
Name::new(name.clone()),
1224+
Gamepad {
1225+
vendor_id: *vendor_id,
1226+
product_id: *product_id,
1227+
..Default::default()
1228+
},
1229+
));
12361230
info!("Gamepad {:?} connected.", id);
12371231
}
12381232
GamepadConnection::Disconnected => {
@@ -1250,6 +1244,9 @@ pub fn gamepad_connection_system(
12501244
}
12511245
}
12521246

1247+
// Note that we don't expose `gilrs::Gamepad::uuid` due to
1248+
// https://gitlab.com/gilrs-project/gilrs/-/issues/153.
1249+
//
12531250
/// The connection status of a gamepad.
12541251
#[derive(Debug, Clone, PartialEq)]
12551252
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
@@ -1260,7 +1257,20 @@ pub fn gamepad_connection_system(
12601257
)]
12611258
pub enum GamepadConnection {
12621259
/// The gamepad is connected.
1263-
Connected(GamepadInfo),
1260+
Connected {
1261+
/// The name of the gamepad.
1262+
///
1263+
/// This name is generally defined by the OS.
1264+
///
1265+
/// For example on Windows the name may be "HID-compliant game controller".
1266+
name: String,
1267+
1268+
/// The USB vendor ID as assigned by the USB-IF, if available.
1269+
vendor_id: Option<u16>,
1270+
1271+
/// The USB product ID as assigned by the vendor, if available.
1272+
product_id: Option<u16>,
1273+
},
12641274
/// The gamepad is disconnected.
12651275
Disconnected,
12661276
}
@@ -1497,8 +1507,8 @@ mod tests {
14971507
GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
14981508
GamepadButtonStateChangedEvent,
14991509
GamepadConnection::{Connected, Disconnected},
1500-
GamepadConnectionEvent, GamepadEvent, GamepadInfo, GamepadSettings,
1501-
RawGamepadAxisChangedEvent, RawGamepadButtonChangedEvent, RawGamepadEvent,
1510+
GamepadConnectionEvent, GamepadEvent, GamepadSettings, RawGamepadAxisChangedEvent,
1511+
RawGamepadButtonChangedEvent, RawGamepadEvent,
15021512
};
15031513
use crate::ButtonState;
15041514
use bevy_app::{App, PreUpdate};
@@ -1871,7 +1881,11 @@ mod tests {
18711881
.resource_mut::<Events<GamepadConnectionEvent>>()
18721882
.send(GamepadConnectionEvent::new(
18731883
gamepad,
1874-
Connected(GamepadInfo::default()),
1884+
Connected {
1885+
name: "Test gamepad".to_string(),
1886+
vendor_id: None,
1887+
product_id: None,
1888+
},
18751889
));
18761890
gamepad
18771891
}

crates/bevy_input/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use gamepad::{
5757
gamepad_connection_system, gamepad_event_processing_system, GamepadAxis,
5858
GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
5959
GamepadButtonStateChangedEvent, GamepadConnection, GamepadConnectionEvent, GamepadEvent,
60-
GamepadInfo, GamepadInput, GamepadRumbleRequest, GamepadSettings, RawGamepadAxisChangedEvent,
60+
GamepadInput, GamepadRumbleRequest, GamepadSettings, RawGamepadAxisChangedEvent,
6161
RawGamepadButtonChangedEvent, RawGamepadEvent,
6262
};
6363

@@ -142,7 +142,6 @@ impl Plugin for InputPlugin {
142142
.register_type::<GamepadButtonChangedEvent>()
143143
.register_type::<GamepadAxisChangedEvent>()
144144
.register_type::<GamepadButtonStateChangedEvent>()
145-
.register_type::<GamepadInfo>()
146145
.register_type::<GamepadConnection>()
147146
.register_type::<GamepadSettings>()
148147
.register_type::<GamepadAxis>()

examples/tools/gamepad_viewer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ fn update_axes(
447447

448448
fn update_connected(
449449
mut connected: EventReader<GamepadConnectionEvent>,
450-
gamepads: Query<(Entity, &Gamepad)>,
450+
gamepads: Query<(Entity, &Name), With<Gamepad>>,
451451
text: Single<Entity, With<ConnectedGamepadsText>>,
452452
mut writer: TextUiWriter,
453453
) {
@@ -458,7 +458,7 @@ fn update_connected(
458458

459459
let formatted = gamepads
460460
.iter()
461-
.map(|(entity, gamepad)| format!("{} - {}", entity, gamepad.info.name))
461+
.map(|(entity, name)| format!("{} - {}", entity, name))
462462
.collect::<Vec<_>>()
463463
.join("\n");
464464

0 commit comments

Comments
 (0)