bevy/examples/input/gamepad_input_events.rs
s-puig e788e3bc83
Implement gamepads as entities (#12770)
# Objective

- Significantly improve the ergonomics of gamepads and allow new
features

Gamepads are a bit unergonomic to work with, they use resources but
unlike other inputs, they are not limited to a single gamepad, to get
around this it uses an identifier (Gamepad) to interact with anything
causing all sorts of issues.

1. There are too many: Gamepads, GamepadSettings, GamepadInfo,
ButtonInput<T>, 2 Axis<T>.
2. ButtonInput/Axis generic methods become really inconvenient to use
e.g. any_pressed()
3. GamepadButton/Axis structs are unnecessary boilerplate:

```rust
for gamepad in gamepads.iter() {
        if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::South)) {
            info!("{:?} just pressed South", gamepad);
        } else if button_inputs.just_released(GamepadButton::new(gamepad, GamepadButtonType::South))
        {
            info!("{:?} just released South", gamepad);
        }
}
```
4. Projects often need to create resources to store the selected gamepad
and have to manually check if their gamepad is still valid anyways.

- Previously attempted by #3419 and #12674


## Solution

- Implement gamepads as entities.

Using entities solves all the problems above and opens new
possibilities.

1. Reduce boilerplate and allows iteration

```rust
let is_pressed = gamepads_buttons.iter().any(|buttons| buttons.pressed(GamepadButtonType::South))
```
2. ButtonInput/Axis generic methods become ergonomic again 
```rust
gamepad_buttons.any_just_pressed([GamepadButtonType::Start, GamepadButtonType::Select])
```
3. Reduces the number of public components significantly (Gamepad,
GamepadSettings, GamepadButtons, GamepadAxes)
4. Components are highly convenient. Gamepad optional features could now
be expressed naturally (`Option<Rumble> or Option<Gyro>`), allows devs
to attach their own components and filter them, so code like this
becomes possible:
```rust
fn move_player<const T: usize>(
    player: Query<&Transform, With<Player<T>>>,
    gamepads_buttons: Query<&GamepadButtons, With<Player<T>>>,
) {
    if let Ok(gamepad_buttons) = gamepads_buttons.get_single() {
        if gamepad_buttons.pressed(GamepadButtonType::South) {
            // move player
        }
    }
}
```
---

## Follow-up

- [ ] Run conditions?
- [ ] Rumble component

# Changelog

## Added

TODO

## Changed

TODO

## Removed

TODO


## Migration Guide

TODO

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-09-27 20:07:20 +00:00

60 lines
2.4 KiB
Rust

//! Iterates and prints gamepad input and connection events.
use bevy::{
input::gamepad::{
GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadButtonStateChangedEvent,
GamepadConnectionEvent, GamepadEvent,
},
prelude::*,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Update, (gamepad_events, gamepad_ordered_events))
.run();
}
fn gamepad_events(
mut connection_events: EventReader<GamepadConnectionEvent>,
// Handles the continuous measure of an axis, equivalent to GamepadAxes::get.
mut axis_changed_events: EventReader<GamepadAxisChangedEvent>,
// Handles the continuous measure of how far a button has been pressed down, equivalent to `GamepadButtons::get`.
mut button_changed_events: EventReader<GamepadButtonChangedEvent>,
// Handles the boolean measure of whether a button is considered pressed or unpressed, as
// defined by the thresholds in `GamepadSettings::button_settings`.
// When the threshold is crossed and the button state changes, this event is emitted.
mut button_input_events: EventReader<GamepadButtonStateChangedEvent>,
) {
for connection_event in connection_events.read() {
info!("{:?}", connection_event);
}
for axis_changed_event in axis_changed_events.read() {
info!(
"{:?} of {:?} is changed to {}",
axis_changed_event.axis, axis_changed_event.entity, axis_changed_event.value
);
}
for button_changed_event in button_changed_events.read() {
info!(
"{:?} of {:?} is changed to {}",
button_changed_event.button, button_changed_event.entity, button_changed_event.value
);
}
for button_input_event in button_input_events.read() {
info!("{:?}", button_input_event);
}
}
// If you require in-frame relative event ordering, you can also read the `Gamepad` event
// stream directly. For standard use-cases, reading the events individually or using the
// `Input<T>` or `Axis<T>` resources is preferable.
fn gamepad_ordered_events(mut gamepad_events: EventReader<GamepadEvent>) {
for gamepad_event in gamepad_events.read() {
match gamepad_event {
GamepadEvent::Connection(connection_event) => info!("{:?}", connection_event),
GamepadEvent::Button(button_event) => info!("{:?}", button_event),
GamepadEvent::Axis(axis_event) => info!("{:?}", axis_event),
}
}
}