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

Add some missing features from the gamepads-as-entities change that were needed to update leafwing-input-manager. #15685

Merged
merged 3 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions crates/bevy_gilrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bevy_time = { path = "../bevy_time", version = "0.15.0-dev" }
# other
gilrs = "0.11.0"
thiserror = "1.0"
uuid = "1"

[lints]
workspace = true
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_gilrs/src/gilrs_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use bevy_input::gamepad::{
RawGamepadButtonChangedEvent, RawGamepadEvent,
};
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
use uuid::Uuid;

pub fn gilrs_event_startup_system(
mut commands: Commands,
Expand All @@ -28,6 +29,9 @@ pub fn gilrs_event_startup_system(

let info = GamepadInfo {
name: gamepad.name().into(),
uuid: Uuid::from_bytes(gamepad.uuid()),
vendor_id: gamepad.vendor_id(),
product_id: gamepad.product_id(),
};

events.send(GamepadConnectionEvent {
Expand Down Expand Up @@ -62,6 +66,9 @@ pub fn gilrs_event_system(

let info = GamepadInfo {
name: pad.name().into(),
uuid: Uuid::from_bytes(pad.uuid()),
vendor_id: pad.vendor_id(),
product_id: pad.product_id(),
};

events.send(
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_input/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
serde = { version = "1", features = ["derive"], optional = true }
thiserror = "1.0"
smol_str = "0.2"
uuid = "1"

[lints]
workspace = true
Expand Down
9 changes: 9 additions & 0 deletions crates/bevy_input/src/axis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ use bevy_ecs::system::Resource;
use bevy_utils::HashMap;
use core::hash::Hash;

#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;

/// Stores the position data of the input devices of type `T`.
///
/// The values are stored as `f32`s, using [`Axis::set`].
/// Use [`Axis::get`] to retrieve the value clamped between [`Axis::MIN`] and [`Axis::MAX`]
/// inclusive, or unclamped using [`Axis::get_unclamped`].
#[derive(Debug, Resource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
pub struct Axis<T> {
/// The position data of the input devices.
axis_data: HashMap<T, f32>,
Expand Down Expand Up @@ -70,6 +74,11 @@ where
pub fn remove(&mut self, input_device: T) -> Option<f32> {
self.axis_data.remove(&input_device)
}

/// Returns an iterator over all axes.
pub fn all_axes(&self) -> impl Iterator<Item = &T> {
pcwalton marked this conversation as resolved.
Show resolved Hide resolved
self.axis_data.keys()
}
}

#[cfg(test)]
Expand Down
78 changes: 76 additions & 2 deletions crates/bevy_input/src/gamepad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use bevy_utils::{
Duration, HashMap,
};
use thiserror::Error;
use uuid::Uuid;

/// A gamepad event.
///
Expand Down Expand Up @@ -364,6 +365,7 @@ pub enum ButtonSettingsError {
/// }
/// ```
#[derive(Component, Debug)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
#[require(GamepadSettings)]
pub struct Gamepad {
info: GamepadInfo,
Expand All @@ -374,8 +376,11 @@ pub struct Gamepad {
}

impl Gamepad {
/// Creates a gamepad with the given metadata
fn new(info: GamepadInfo) -> Self {
/// Creates a gamepad with the given metadata.
///
/// Ordinarily, you shouldn't call this, but it can be useful for test
/// mocking.
pub fn new(info: GamepadInfo) -> Self {
pcwalton marked this conversation as resolved.
Show resolved Hide resolved
let mut analog = Axis::default();
for button in GamepadButton::all().iter().copied() {
analog.set(button.into(), 0.0);
Expand All @@ -399,6 +404,27 @@ impl Gamepad {
self.info.name.as_str()
}

/// The UUID of the game pad.
///
/// Bevy will try to reuse IDs for game controllers across invocations of
pcwalton marked this conversation as resolved.
Show resolved Hide resolved
/// the app. Therefore, this ID can be used to track the gamepad across
/// runs.
pub fn uuid(&self) -> &Uuid {
&self.info.uuid
}

/// Returns the USB vendor ID as assigned by the USB-IF, if available.
pub fn vendor_id(&self) -> Option<u16> {
self.info.vendor_id
}

/// Returns the USB product ID as assigned by the [vendor], if available.
///
/// [vendor]: Self::vendor_id
pub fn product_id(&self) -> Option<u16> {
self.info.product_id
}

/// Returns the analog data of the provided [`GamepadAxis`] or [`GamepadButton`].
///
/// This will be clamped between [[`Axis::MIN`],[`Axis::MAX`]].
Expand Down Expand Up @@ -505,6 +531,34 @@ impl Gamepad {
.into_iter()
.all(|button_type| self.just_released(button_type))
}

/// Returns an iterator over all digital [button]s that are pressed.
///
/// [button]: GamepadButton
pub fn get_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking bikeshed question: Should these iterator methods instead be called iter_pressed, iter_just_pressed etc.?

Copy link
Member

@alice-i-cecile alice-i-cecile Oct 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be consistent with the ButtonInput API: https://docs.rs/bevy/latest/bevy/input/struct.ButtonInput.html#method.

I do think this might be nice to change, but it should be done all at once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfectly reasonable!

self.digital.get_pressed()
}

/// Returns an iterator over all digital [button]s that were just pressed.
///
/// [button]: GamepadButton
pub fn get_just_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
self.digital.get_just_pressed()
}

/// Returns an iterator over all digital [button]s that were just released.
///
/// [button]: GamepadButton
pub fn get_just_released(&self) -> impl Iterator<Item = &GamepadButton> {
self.digital.get_just_released()
}

/// Returns an iterator over all analog [axes].
///
/// [axes]: GamepadInput
pub fn get_analog_axes(&self) -> impl Iterator<Item = &GamepadInput> {
self.analog.all_axes()
}
}

/// Metadata associated with a [`Gamepad`].
Expand All @@ -522,6 +576,21 @@ pub struct GamepadInfo {
///
/// For example on Windows the name may be "HID-compliant game controller".
pub name: String,

/// The UUID of the game pad.
///
/// Bevy will try to reuse IDs for game controllers across invocations of
/// the app. Therefore, this ID can be used to track the gamepad across
/// runs.
pub uuid: Uuid,

/// The USB vendor ID as assigned by the USB-IF, if available.
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>,
}

/// Represents gamepad input types that are mapped in the range [0.0, 1.0].
Expand Down Expand Up @@ -665,6 +734,7 @@ impl GamepadAxis {
/// Encapsulation over [`GamepadAxis`] and [`GamepadButton`]
// This is done so Gamepad can share a single Axis<T> and simplifies the API by having only one get/get_unclamped method
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
pub enum GamepadInput {
/// A [`GamepadAxis`]
Axis(GamepadAxis),
Expand Down Expand Up @@ -1620,6 +1690,7 @@ mod tests {
use bevy_ecs::entity::Entity;
use bevy_ecs::event::Events;
use bevy_ecs::schedule::IntoSystemConfigs;
use uuid::Uuid;

fn test_button_axis_settings_filter(
settings: ButtonAxisSettings,
Expand Down Expand Up @@ -1988,6 +2059,9 @@ mod tests {
gamepad,
Connected(GamepadInfo {
name: String::from("Gamepad test"),
uuid: Uuid::parse_str("6d860618-c538-4d51-b0a5-0959b9f8c670").unwrap(),
vendor_id: None,
product_id: None,
}),
));
gamepad
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_input/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ use mouse::{
};
use touch::{touch_screen_input_system, TouchInput, Touches};

#[cfg(feature = "bevy_reflect")]
use gamepad::Gamepad;
use gamepad::{
gamepad_connection_system, gamepad_event_processing_system, GamepadAxisChangedEvent,
GamepadButtonChangedEvent, GamepadButtonStateChangedEvent, GamepadConnection,
Expand Down Expand Up @@ -134,6 +136,7 @@ impl Plugin for InputPlugin {
.register_type::<RawGamepadEvent>()
.register_type::<RawGamepadAxisChangedEvent>()
.register_type::<RawGamepadButtonChangedEvent>()
.register_type::<Gamepad>()
pcwalton marked this conversation as resolved.
Show resolved Hide resolved
.register_type::<GamepadConnectionEvent>()
.register_type::<GamepadButtonChangedEvent>()
.register_type::<GamepadAxisChangedEvent>()
Expand Down