bevy/crates/bevy_gilrs/src/converter.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

41 lines
2.2 KiB
Rust

use bevy_input::gamepad::{GamepadAxis, GamepadButton};
pub fn convert_button(button: gilrs::Button) -> Option<GamepadButton> {
match button {
gilrs::Button::South => Some(GamepadButton::South),
gilrs::Button::East => Some(GamepadButton::East),
gilrs::Button::North => Some(GamepadButton::North),
gilrs::Button::West => Some(GamepadButton::West),
gilrs::Button::C => Some(GamepadButton::C),
gilrs::Button::Z => Some(GamepadButton::Z),
gilrs::Button::LeftTrigger => Some(GamepadButton::LeftTrigger),
gilrs::Button::LeftTrigger2 => Some(GamepadButton::LeftTrigger2),
gilrs::Button::RightTrigger => Some(GamepadButton::RightTrigger),
gilrs::Button::RightTrigger2 => Some(GamepadButton::RightTrigger2),
gilrs::Button::Select => Some(GamepadButton::Select),
gilrs::Button::Start => Some(GamepadButton::Start),
gilrs::Button::Mode => Some(GamepadButton::Mode),
gilrs::Button::LeftThumb => Some(GamepadButton::LeftThumb),
gilrs::Button::RightThumb => Some(GamepadButton::RightThumb),
gilrs::Button::DPadUp => Some(GamepadButton::DPadUp),
gilrs::Button::DPadDown => Some(GamepadButton::DPadDown),
gilrs::Button::DPadLeft => Some(GamepadButton::DPadLeft),
gilrs::Button::DPadRight => Some(GamepadButton::DPadRight),
gilrs::Button::Unknown => None,
}
}
pub fn convert_axis(axis: gilrs::Axis) -> Option<GamepadAxis> {
match axis {
gilrs::Axis::LeftStickX => Some(GamepadAxis::LeftStickX),
gilrs::Axis::LeftStickY => Some(GamepadAxis::LeftStickY),
gilrs::Axis::LeftZ => Some(GamepadAxis::LeftZ),
gilrs::Axis::RightStickX => Some(GamepadAxis::RightStickX),
gilrs::Axis::RightStickY => Some(GamepadAxis::RightStickY),
gilrs::Axis::RightZ => Some(GamepadAxis::RightZ),
// The `axis_dpad_to_button` gilrs filter should filter out all DPadX and DPadY events. If
// it doesn't then we probably need an entry added to the following repo and an update to
// GilRs to use the updated database: https://github.com/gabomdq/SDL_GameControllerDB
gilrs::Axis::Unknown | gilrs::Axis::DPadX | gilrs::Axis::DPadY => None,
}
}