diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 9ba8e018e27bc..7bdf4e62f95e4 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -1,12 +1,42 @@ use crate::{Axis, Input}; use bevy_app::{EventReader, EventWriter}; use bevy_ecs::system::{Res, ResMut}; -use bevy_utils::HashMap; +use bevy_utils::{tracing::info, HashMap, HashSet}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct Gamepad(pub usize); +#[derive(Default)] +/// Container of unique connected [Gamepad]s +/// +/// [Gamepad]s are registered and deregistered in [gamepad_connection_system] +pub struct Gamepads { + gamepads: HashSet, +} + +impl Gamepads { + /// Returns true if the [Gamepads] contains a [Gamepad]. + pub fn contains(&self, gamepad: &Gamepad) -> bool { + self.gamepads.contains(gamepad) + } + + /// Iterates over registered [Gamepad]s + pub fn iter(&self) -> impl Iterator + '_ { + self.gamepads.iter() + } + + /// Registers [Gamepad]. + fn register(&mut self, gamepad: Gamepad) { + self.gamepads.insert(gamepad); + } + + /// Deregisters [Gamepad. + fn deregister(&mut self, gamepad: &Gamepad) { + self.gamepads.remove(gamepad); + } +} + #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum GamepadEventType { @@ -201,6 +231,28 @@ impl ButtonAxisSettings { } } +/// Monitors gamepad connection and disconnection events, updating the [GamepadLobby] resource accordingly +/// +/// By default, runs during `CoreStage::PreUpdate` when added via [InputPlugin]. +pub fn gamepad_connection_system( + mut gamepads: ResMut, + mut gamepad_event: EventReader, +) { + for event in gamepad_event.iter() { + match &event { + GamepadEvent(gamepad, GamepadEventType::Connected) => { + gamepads.register(*gamepad); + info!("{:?} Connected", gamepad); + } + GamepadEvent(gamepad, GamepadEventType::Disconnected) => { + gamepads.deregister(gamepad); + info!("{:?} Disconnected", gamepad); + } + _ => (), + } + } +} + pub fn gamepad_event_system( mut button_input: ResMut>, mut axis: ResMut>, diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 65c70d754912e..a9dbc2d3301d3 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -15,7 +15,7 @@ pub mod prelude { pub use crate::{ gamepad::{ Gamepad, GamepadAxis, GamepadAxisType, GamepadButton, GamepadButtonType, GamepadEvent, - GamepadEventType, + GamepadEventType, Gamepads, }, keyboard::KeyCode, mouse::MouseButton, @@ -27,11 +27,12 @@ pub mod prelude { use bevy_app::prelude::*; use keyboard::{keyboard_input_system, KeyCode, KeyboardInput}; use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel}; +use prelude::Gamepads; use touch::{touch_screen_input_system, TouchInput, Touches}; use gamepad::{ - gamepad_event_system, GamepadAxis, GamepadButton, GamepadEvent, GamepadEventRaw, - GamepadSettings, + gamepad_connection_system, gamepad_event_system, GamepadAxis, GamepadButton, GamepadEvent, + GamepadEventRaw, GamepadSettings, }; /// Adds keyboard and mouse input to an App @@ -64,6 +65,7 @@ impl Plugin for InputPlugin { .add_event::() .add_event::() .init_resource::() + .init_resource::() .init_resource::>() .init_resource::>() .init_resource::>() @@ -71,6 +73,10 @@ impl Plugin for InputPlugin { CoreStage::PreUpdate, gamepad_event_system.label(InputSystem), ) + .add_system_to_stage( + CoreStage::PreUpdate, + gamepad_connection_system.label(InputSystem), + ) // touch .add_event::() .init_resource::() diff --git a/examples/input/gamepad_input.rs b/examples/input/gamepad_input.rs index 24bd872f48ab8..8f543629ba79e 100644 --- a/examples/input/gamepad_input.rs +++ b/examples/input/gamepad_input.rs @@ -1,49 +1,19 @@ -use bevy::{ - input::gamepad::{Gamepad, GamepadButton, GamepadEvent, GamepadEventType}, - prelude::*, - utils::HashSet, -}; +use bevy::{input::gamepad::GamepadButton, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) - .init_resource::() - .add_system_to_stage(CoreStage::PreUpdate, connection_system) .add_system(gamepad_system) .run(); } -#[derive(Default)] -struct GamepadLobby { - gamepads: HashSet, -} - -fn connection_system( - mut lobby: ResMut, - mut gamepad_event: EventReader, -) { - for event in gamepad_event.iter() { - match &event { - GamepadEvent(gamepad, GamepadEventType::Connected) => { - lobby.gamepads.insert(*gamepad); - info!("{:?} Connected", gamepad); - } - GamepadEvent(gamepad, GamepadEventType::Disconnected) => { - lobby.gamepads.remove(gamepad); - info!("{:?} Disconnected", gamepad); - } - _ => (), - } - } -} - fn gamepad_system( - lobby: Res, + gamepads: Res, button_inputs: Res>, button_axes: Res>, axes: Res>, ) { - for gamepad in lobby.gamepads.iter().cloned() { + for gamepad in gamepads.iter().cloned() { if button_inputs.just_pressed(GamepadButton(gamepad, GamepadButtonType::South)) { info!("{:?} just pressed South", gamepad); } else if button_inputs.just_released(GamepadButton(gamepad, GamepadButtonType::South)) {