mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
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>
This commit is contained in:
parent
39d6a745d2
commit
e788e3bc83
12 changed files with 1665 additions and 797 deletions
|
@ -1,42 +1,38 @@
|
|||
use bevy_input::gamepad::{Gamepad, GamepadAxisType, GamepadButtonType};
|
||||
use bevy_input::gamepad::{GamepadAxis, GamepadButton};
|
||||
|
||||
pub fn convert_gamepad_id(gamepad_id: gilrs::GamepadId) -> Gamepad {
|
||||
Gamepad::new(gamepad_id.into())
|
||||
}
|
||||
|
||||
pub fn convert_button(button: gilrs::Button) -> Option<GamepadButtonType> {
|
||||
pub fn convert_button(button: gilrs::Button) -> Option<GamepadButton> {
|
||||
match button {
|
||||
gilrs::Button::South => Some(GamepadButtonType::South),
|
||||
gilrs::Button::East => Some(GamepadButtonType::East),
|
||||
gilrs::Button::North => Some(GamepadButtonType::North),
|
||||
gilrs::Button::West => Some(GamepadButtonType::West),
|
||||
gilrs::Button::C => Some(GamepadButtonType::C),
|
||||
gilrs::Button::Z => Some(GamepadButtonType::Z),
|
||||
gilrs::Button::LeftTrigger => Some(GamepadButtonType::LeftTrigger),
|
||||
gilrs::Button::LeftTrigger2 => Some(GamepadButtonType::LeftTrigger2),
|
||||
gilrs::Button::RightTrigger => Some(GamepadButtonType::RightTrigger),
|
||||
gilrs::Button::RightTrigger2 => Some(GamepadButtonType::RightTrigger2),
|
||||
gilrs::Button::Select => Some(GamepadButtonType::Select),
|
||||
gilrs::Button::Start => Some(GamepadButtonType::Start),
|
||||
gilrs::Button::Mode => Some(GamepadButtonType::Mode),
|
||||
gilrs::Button::LeftThumb => Some(GamepadButtonType::LeftThumb),
|
||||
gilrs::Button::RightThumb => Some(GamepadButtonType::RightThumb),
|
||||
gilrs::Button::DPadUp => Some(GamepadButtonType::DPadUp),
|
||||
gilrs::Button::DPadDown => Some(GamepadButtonType::DPadDown),
|
||||
gilrs::Button::DPadLeft => Some(GamepadButtonType::DPadLeft),
|
||||
gilrs::Button::DPadRight => Some(GamepadButtonType::DPadRight),
|
||||
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<GamepadAxisType> {
|
||||
pub fn convert_axis(axis: gilrs::Axis) -> Option<GamepadAxis> {
|
||||
match axis {
|
||||
gilrs::Axis::LeftStickX => Some(GamepadAxisType::LeftStickX),
|
||||
gilrs::Axis::LeftStickY => Some(GamepadAxisType::LeftStickY),
|
||||
gilrs::Axis::LeftZ => Some(GamepadAxisType::LeftZ),
|
||||
gilrs::Axis::RightStickX => Some(GamepadAxisType::RightStickX),
|
||||
gilrs::Axis::RightStickY => Some(GamepadAxisType::RightStickY),
|
||||
gilrs::Axis::RightZ => Some(GamepadAxisType::RightZ),
|
||||
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
|
||||
|
|
|
@ -1,103 +1,113 @@
|
|||
use crate::{
|
||||
converter::{convert_axis, convert_button, convert_gamepad_id},
|
||||
Gilrs,
|
||||
converter::{convert_axis, convert_button},
|
||||
Gilrs, GilrsGamepads,
|
||||
};
|
||||
use bevy_ecs::event::EventWriter;
|
||||
use bevy_ecs::prelude::Commands;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use bevy_ecs::system::NonSendMut;
|
||||
use bevy_ecs::{
|
||||
event::EventWriter,
|
||||
system::{Res, ResMut},
|
||||
};
|
||||
use bevy_input::{
|
||||
gamepad::{
|
||||
GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadConnection,
|
||||
GamepadConnectionEvent, GamepadEvent, GamepadInfo, GamepadSettings,
|
||||
},
|
||||
prelude::{GamepadAxis, GamepadButton},
|
||||
Axis,
|
||||
use bevy_ecs::system::ResMut;
|
||||
use bevy_input::gamepad::{
|
||||
GamepadConnection, GamepadConnectionEvent, GamepadInfo, RawGamepadAxisChangedEvent,
|
||||
RawGamepadButtonChangedEvent, RawGamepadEvent,
|
||||
};
|
||||
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
|
||||
|
||||
pub fn gilrs_event_startup_system(
|
||||
mut commands: Commands,
|
||||
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
|
||||
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
|
||||
mut events: EventWriter<GamepadEvent>,
|
||||
mut gamepads: ResMut<GilrsGamepads>,
|
||||
mut events: EventWriter<GamepadConnectionEvent>,
|
||||
) {
|
||||
for (id, gamepad) in gilrs.0.get().gamepads() {
|
||||
// Create entity and add to mapping
|
||||
let entity = commands.spawn_empty().id();
|
||||
gamepads.id_to_entity.insert(id, entity);
|
||||
gamepads.entity_to_id.insert(entity, id);
|
||||
|
||||
let info = GamepadInfo {
|
||||
name: gamepad.name().into(),
|
||||
};
|
||||
|
||||
events.send(
|
||||
GamepadConnectionEvent {
|
||||
gamepad: convert_gamepad_id(id),
|
||||
connection: GamepadConnection::Connected(info),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
events.send(GamepadConnectionEvent {
|
||||
gamepad: entity,
|
||||
connection: GamepadConnection::Connected(info),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gilrs_event_system(
|
||||
mut commands: Commands,
|
||||
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
|
||||
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
|
||||
mut events: EventWriter<GamepadEvent>,
|
||||
mut gamepad_buttons: ResMut<Axis<GamepadButton>>,
|
||||
gamepad_axis: Res<Axis<GamepadAxis>>,
|
||||
gamepad_settings: Res<GamepadSettings>,
|
||||
mut gamepads: ResMut<GilrsGamepads>,
|
||||
mut events: EventWriter<RawGamepadEvent>,
|
||||
mut connection_events: EventWriter<GamepadConnectionEvent>,
|
||||
mut button_events: EventWriter<RawGamepadButtonChangedEvent>,
|
||||
mut axis_event: EventWriter<RawGamepadAxisChangedEvent>,
|
||||
) {
|
||||
let gilrs = gilrs.0.get();
|
||||
while let Some(gilrs_event) = gilrs.next_event().filter_ev(&axis_dpad_to_button, gilrs) {
|
||||
gilrs.update(&gilrs_event);
|
||||
|
||||
let gamepad = convert_gamepad_id(gilrs_event.id);
|
||||
match gilrs_event.event {
|
||||
EventType::Connected => {
|
||||
let pad = gilrs.gamepad(gilrs_event.id);
|
||||
let entity = gamepads.get_entity(gilrs_event.id).unwrap_or_else(|| {
|
||||
let entity = commands.spawn_empty().id();
|
||||
gamepads.id_to_entity.insert(gilrs_event.id, entity);
|
||||
gamepads.entity_to_id.insert(entity, gilrs_event.id);
|
||||
entity
|
||||
});
|
||||
|
||||
let info = GamepadInfo {
|
||||
name: pad.name().into(),
|
||||
};
|
||||
|
||||
events.send(
|
||||
GamepadConnectionEvent::new(gamepad, GamepadConnection::Connected(info)).into(),
|
||||
GamepadConnectionEvent::new(entity, GamepadConnection::Connected(info.clone()))
|
||||
.into(),
|
||||
);
|
||||
connection_events.send(GamepadConnectionEvent::new(
|
||||
entity,
|
||||
GamepadConnection::Connected(info),
|
||||
));
|
||||
}
|
||||
EventType::Disconnected => {
|
||||
events.send(
|
||||
GamepadConnectionEvent::new(gamepad, GamepadConnection::Disconnected).into(),
|
||||
);
|
||||
let gamepad = gamepads
|
||||
.id_to_entity
|
||||
.get(&gilrs_event.id)
|
||||
.copied()
|
||||
.expect("mapping should exist from connection");
|
||||
let event = GamepadConnectionEvent::new(gamepad, GamepadConnection::Disconnected);
|
||||
events.send(event.clone().into());
|
||||
connection_events.send(event);
|
||||
}
|
||||
EventType::ButtonChanged(gilrs_button, raw_value, _) => {
|
||||
if let Some(button_type) = convert_button(gilrs_button) {
|
||||
let button = GamepadButton::new(gamepad, button_type);
|
||||
let old_value = gamepad_buttons.get(button);
|
||||
let button_settings = gamepad_settings.get_button_axis_settings(button);
|
||||
|
||||
// Only send events that pass the user-defined change threshold
|
||||
if let Some(filtered_value) = button_settings.filter(raw_value, old_value) {
|
||||
events.send(
|
||||
GamepadButtonChangedEvent::new(gamepad, button_type, filtered_value)
|
||||
.into(),
|
||||
);
|
||||
// Update the current value prematurely so that `old_value` is correct in
|
||||
// future iterations of the loop.
|
||||
gamepad_buttons.set(button, filtered_value);
|
||||
}
|
||||
}
|
||||
let Some(button) = convert_button(gilrs_button) else {
|
||||
continue;
|
||||
};
|
||||
let gamepad = gamepads
|
||||
.id_to_entity
|
||||
.get(&gilrs_event.id)
|
||||
.copied()
|
||||
.expect("mapping should exist from connection");
|
||||
events.send(RawGamepadButtonChangedEvent::new(gamepad, button, raw_value).into());
|
||||
button_events.send(RawGamepadButtonChangedEvent::new(
|
||||
gamepad, button, raw_value,
|
||||
));
|
||||
}
|
||||
EventType::AxisChanged(gilrs_axis, raw_value, _) => {
|
||||
if let Some(axis_type) = convert_axis(gilrs_axis) {
|
||||
let axis = GamepadAxis::new(gamepad, axis_type);
|
||||
let old_value = gamepad_axis.get(axis);
|
||||
let axis_settings = gamepad_settings.get_axis_settings(axis);
|
||||
|
||||
// Only send events that pass the user-defined change threshold
|
||||
if let Some(filtered_value) = axis_settings.filter(raw_value, old_value) {
|
||||
events.send(
|
||||
GamepadAxisChangedEvent::new(gamepad, axis_type, filtered_value).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
let Some(axis) = convert_axis(gilrs_axis) else {
|
||||
continue;
|
||||
};
|
||||
let gamepad = gamepads
|
||||
.id_to_entity
|
||||
.get(&gilrs_event.id)
|
||||
.copied()
|
||||
.expect("mapping should exist from connection");
|
||||
events.send(RawGamepadAxisChangedEvent::new(gamepad, axis, raw_value).into());
|
||||
axis_event.send(RawGamepadAxisChangedEvent::new(gamepad, axis, raw_value));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
|
|
@ -15,9 +15,10 @@ mod gilrs_system;
|
|||
mod rumble;
|
||||
|
||||
use bevy_app::{App, Plugin, PostUpdate, PreStartup, PreUpdate};
|
||||
use bevy_ecs::entity::EntityHashMap;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_input::InputSystem;
|
||||
use bevy_utils::{synccell::SyncCell, tracing::error};
|
||||
use bevy_utils::{synccell::SyncCell, tracing::error, HashMap};
|
||||
use gilrs::GilrsBuilder;
|
||||
use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
|
||||
use rumble::{play_gilrs_rumble, RunningRumbleEffects};
|
||||
|
@ -25,6 +26,27 @@ use rumble::{play_gilrs_rumble, RunningRumbleEffects};
|
|||
#[cfg_attr(not(target_arch = "wasm32"), derive(Resource))]
|
||||
pub(crate) struct Gilrs(pub SyncCell<gilrs::Gilrs>);
|
||||
|
||||
/// A [`resource`](Resource) with the mapping of connected [`gilrs::GamepadId`] and their [`Entity`].
|
||||
#[derive(Debug, Default, Resource)]
|
||||
pub(crate) struct GilrsGamepads {
|
||||
/// Mapping of [`Entity`] to [`gilrs::GamepadId`].
|
||||
pub(crate) entity_to_id: EntityHashMap<gilrs::GamepadId>,
|
||||
/// Mapping of [`gilrs::GamepadId`] to [`Entity`].
|
||||
pub(crate) id_to_entity: HashMap<gilrs::GamepadId, Entity>,
|
||||
}
|
||||
|
||||
impl GilrsGamepads {
|
||||
/// Returns the [`Entity`] assigned to a connected [`gilrs::GamepadId`].
|
||||
pub fn get_entity(&self, gamepad_id: gilrs::GamepadId) -> Option<Entity> {
|
||||
self.id_to_entity.get(&gamepad_id).copied()
|
||||
}
|
||||
|
||||
/// Returns the [`gilrs::GamepadId`] assigned to a gamepad [`Entity`].
|
||||
pub fn get_gamepad_id(&self, entity: Entity) -> Option<gilrs::GamepadId> {
|
||||
self.entity_to_id.get(&entity).copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// Plugin that provides gamepad handling to an [`App`].
|
||||
#[derive(Default)]
|
||||
pub struct GilrsPlugin;
|
||||
|
@ -45,7 +67,7 @@ impl Plugin for GilrsPlugin {
|
|||
app.insert_non_send_resource(Gilrs(SyncCell::new(gilrs)));
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
app.insert_resource(Gilrs(SyncCell::new(gilrs)));
|
||||
|
||||
app.init_resource::<GilrsGamepads>();
|
||||
app.init_resource::<RunningRumbleEffects>()
|
||||
.add_systems(PreStartup, gilrs_event_startup_system)
|
||||
.add_systems(PreUpdate, gilrs_event_system.before(InputSystem))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Handle user specified rumble request events.
|
||||
use crate::Gilrs;
|
||||
use crate::{Gilrs, GilrsGamepads};
|
||||
use bevy_ecs::prelude::{EventReader, Res, ResMut, Resource};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use bevy_ecs::system::NonSendMut;
|
||||
|
@ -16,8 +16,6 @@ use gilrs::{
|
|||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::converter::convert_gamepad_id;
|
||||
|
||||
/// A rumble effect that is currently in effect.
|
||||
struct RunningRumble {
|
||||
/// Duration from app startup when this effect will be finished
|
||||
|
@ -84,6 +82,7 @@ fn get_base_effects(
|
|||
fn handle_rumble_request(
|
||||
running_rumbles: &mut RunningRumbleEffects,
|
||||
gilrs: &mut gilrs::Gilrs,
|
||||
gamepads: &GilrsGamepads,
|
||||
rumble: GamepadRumbleRequest,
|
||||
current_time: Duration,
|
||||
) -> Result<(), RumbleError> {
|
||||
|
@ -91,7 +90,7 @@ fn handle_rumble_request(
|
|||
|
||||
let (gamepad_id, _) = gilrs
|
||||
.gamepads()
|
||||
.find(|(pad_id, _)| convert_gamepad_id(*pad_id) == gamepad)
|
||||
.find(|(pad_id, _)| *pad_id == gamepads.get_gamepad_id(gamepad).unwrap())
|
||||
.ok_or(RumbleError::GamepadNotFound)?;
|
||||
|
||||
match rumble {
|
||||
|
@ -129,6 +128,7 @@ pub(crate) fn play_gilrs_rumble(
|
|||
time: Res<Time<Real>>,
|
||||
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
|
||||
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
|
||||
gamepads: Res<GilrsGamepads>,
|
||||
mut requests: EventReader<GamepadRumbleRequest>,
|
||||
mut running_rumbles: ResMut<RunningRumbleEffects>,
|
||||
) {
|
||||
|
@ -146,7 +146,7 @@ pub(crate) fn play_gilrs_rumble(
|
|||
// Add new effects.
|
||||
for rumble in requests.read().cloned() {
|
||||
let gamepad = rumble.gamepad();
|
||||
match handle_rumble_request(&mut running_rumbles, gilrs, rumble, current_time) {
|
||||
match handle_rumble_request(&mut running_rumbles, gilrs, &gamepads, rumble, current_time) {
|
||||
Ok(()) => {}
|
||||
Err(RumbleError::GilrsError(err)) => {
|
||||
if let ff::Error::FfNotSupported(_) = err {
|
||||
|
|
|
@ -57,7 +57,7 @@ where
|
|||
|
||||
/// Returns the unclamped position data of the provided `input_device`.
|
||||
///
|
||||
/// This value may be outside of the [`Axis::MIN`] and [`Axis::MAX`] range.
|
||||
/// This value may be outside the [`Axis::MIN`] and [`Axis::MAX`] range.
|
||||
///
|
||||
/// Use for things like camera zoom, where you want devices like mouse wheels to be able to
|
||||
/// exceed the normal range. If being able to move faster on one input device
|
||||
|
@ -70,18 +70,11 @@ where
|
|||
pub fn remove(&mut self, input_device: T) -> Option<f32> {
|
||||
self.axis_data.remove(&input_device)
|
||||
}
|
||||
/// Returns an iterator of all the input devices that have position data
|
||||
pub fn devices(&self) -> impl ExactSizeIterator<Item = &T> {
|
||||
self.axis_data.keys()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
gamepad::{Gamepad, GamepadButton, GamepadButtonType},
|
||||
Axis,
|
||||
};
|
||||
use crate::{gamepad::GamepadButton, Axis};
|
||||
|
||||
#[test]
|
||||
fn test_axis_set() {
|
||||
|
@ -100,13 +93,11 @@ mod tests {
|
|||
];
|
||||
|
||||
for (value, expected) in cases {
|
||||
let gamepad_button =
|
||||
GamepadButton::new(Gamepad::new(1), GamepadButtonType::RightTrigger);
|
||||
let mut axis = Axis::<GamepadButton>::default();
|
||||
|
||||
axis.set(gamepad_button, value);
|
||||
axis.set(GamepadButton::RightTrigger, value);
|
||||
|
||||
let actual = axis.get(gamepad_button);
|
||||
let actual = axis.get(GamepadButton::RightTrigger);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
||||
|
@ -116,48 +107,16 @@ mod tests {
|
|||
let cases = [-1.0, -0.9, -0.1, 0.0, 0.1, 0.9, 1.0];
|
||||
|
||||
for value in cases {
|
||||
let gamepad_button =
|
||||
GamepadButton::new(Gamepad::new(1), GamepadButtonType::RightTrigger);
|
||||
let mut axis = Axis::<GamepadButton>::default();
|
||||
|
||||
axis.set(gamepad_button, value);
|
||||
assert!(axis.get(gamepad_button).is_some());
|
||||
axis.set(GamepadButton::RightTrigger, value);
|
||||
assert!(axis.get(GamepadButton::RightTrigger).is_some());
|
||||
|
||||
axis.remove(gamepad_button);
|
||||
let actual = axis.get(gamepad_button);
|
||||
axis.remove(GamepadButton::RightTrigger);
|
||||
let actual = axis.get(GamepadButton::RightTrigger);
|
||||
let expected = None;
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_axis_devices() {
|
||||
let mut axis = Axis::<GamepadButton>::default();
|
||||
assert_eq!(axis.devices().count(), 0);
|
||||
|
||||
axis.set(
|
||||
GamepadButton::new(Gamepad::new(1), GamepadButtonType::RightTrigger),
|
||||
0.1,
|
||||
);
|
||||
assert_eq!(axis.devices().count(), 1);
|
||||
|
||||
axis.set(
|
||||
GamepadButton::new(Gamepad::new(1), GamepadButtonType::LeftTrigger),
|
||||
0.5,
|
||||
);
|
||||
assert_eq!(axis.devices().count(), 2);
|
||||
|
||||
axis.set(
|
||||
GamepadButton::new(Gamepad::new(1), GamepadButtonType::RightTrigger),
|
||||
-0.1,
|
||||
);
|
||||
assert_eq!(axis.devices().count(), 2);
|
||||
|
||||
axis.remove(GamepadButton::new(
|
||||
Gamepad::new(1),
|
||||
GamepadButtonType::RightTrigger,
|
||||
));
|
||||
assert_eq!(axis.devices().count(), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,17 +73,13 @@ use {
|
|||
/// ```no_run
|
||||
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
|
||||
/// # use bevy_ecs::{prelude::{IntoSystemConfigs, Res, Resource, resource_changed}, schedule::Condition};
|
||||
/// # use bevy_input::{ButtonInput, prelude::{GamepadButton, KeyCode, MouseButton}};
|
||||
/// # use bevy_input::{ButtonInput, prelude::{KeyCode, MouseButton}};
|
||||
///
|
||||
/// fn main() {
|
||||
/// App::new()
|
||||
/// .add_plugins(DefaultPlugins)
|
||||
/// .add_systems(
|
||||
/// Update,
|
||||
/// print_gamepad.run_if(resource_changed::<ButtonInput<GamepadButton>>),
|
||||
/// )
|
||||
/// .add_systems(
|
||||
/// Update,
|
||||
/// print_mouse.run_if(resource_changed::<ButtonInput<MouseButton>>),
|
||||
/// )
|
||||
/// .add_systems(
|
||||
|
@ -93,10 +89,6 @@ use {
|
|||
/// .run();
|
||||
/// }
|
||||
///
|
||||
/// fn print_gamepad(gamepad: Res<ButtonInput<GamepadButton>>) {
|
||||
/// println!("Gamepad: {:?}", gamepad.get_pressed().collect::<Vec<_>>());
|
||||
/// }
|
||||
///
|
||||
/// fn print_mouse(mouse: Res<ButtonInput<MouseButton>>) {
|
||||
/// println!("Mouse: {:?}", mouse.get_pressed().collect::<Vec<_>>());
|
||||
/// }
|
||||
|
@ -115,33 +107,6 @@ use {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Accepting input from multiple devices:
|
||||
/// ```no_run
|
||||
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
|
||||
/// # use bevy_ecs::{prelude::IntoSystemConfigs, schedule::Condition};
|
||||
/// # use bevy_input::{ButtonInput, common_conditions::{input_just_pressed}, prelude::{GamepadButton, Gamepad, GamepadButtonType, KeyCode}};
|
||||
///
|
||||
/// fn main() {
|
||||
/// App::new()
|
||||
/// .add_plugins(DefaultPlugins)
|
||||
/// .add_systems(
|
||||
/// Update,
|
||||
/// something_used.run_if(
|
||||
/// input_just_pressed(KeyCode::KeyE)
|
||||
/// .or(input_just_pressed(GamepadButton::new(
|
||||
/// Gamepad::new(0),
|
||||
/// GamepadButtonType::West,
|
||||
/// ))),
|
||||
/// ),
|
||||
/// )
|
||||
/// .run();
|
||||
/// }
|
||||
///
|
||||
/// fn something_used() {
|
||||
/// println!("Generic use-ish button pressed.");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// When adding this resource for a new input type, you should:
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,9 +30,7 @@ pub use button_input::*;
|
|||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
gamepad::{
|
||||
Gamepad, GamepadAxis, GamepadAxisType, GamepadButton, GamepadButtonType, Gamepads,
|
||||
},
|
||||
gamepad::{Gamepad, GamepadAxis, GamepadButton, GamepadSettings},
|
||||
keyboard::KeyCode,
|
||||
mouse::MouseButton,
|
||||
touch::{TouchInput, Touches},
|
||||
|
@ -54,10 +52,10 @@ use mouse::{
|
|||
use touch::{touch_screen_input_system, TouchInput, Touches};
|
||||
|
||||
use gamepad::{
|
||||
gamepad_axis_event_system, gamepad_button_event_system, gamepad_connection_system,
|
||||
gamepad_event_system, GamepadAxis, GamepadAxisChangedEvent, GamepadButton,
|
||||
GamepadButtonChangedEvent, GamepadButtonInput, GamepadConnectionEvent, GamepadEvent,
|
||||
GamepadRumbleRequest, GamepadSettings, Gamepads,
|
||||
gamepad_connection_system, gamepad_event_processing_system, GamepadAxisChangedEvent,
|
||||
GamepadButtonChangedEvent, GamepadButtonStateChangedEvent, GamepadConnection,
|
||||
GamepadConnectionEvent, GamepadEvent, GamepadInfo, GamepadRumbleRequest, GamepadSettings,
|
||||
RawGamepadAxisChangedEvent, RawGamepadButtonChangedEvent, RawGamepadEvent,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
|
||||
|
@ -98,30 +96,22 @@ impl Plugin for InputPlugin {
|
|||
.add_event::<DoubleTapGesture>()
|
||||
.add_event::<PanGesture>()
|
||||
// gamepad
|
||||
.add_event::<GamepadEvent>()
|
||||
.add_event::<GamepadConnectionEvent>()
|
||||
.add_event::<GamepadButtonChangedEvent>()
|
||||
.add_event::<GamepadButtonInput>()
|
||||
.add_event::<GamepadButtonStateChangedEvent>()
|
||||
.add_event::<GamepadAxisChangedEvent>()
|
||||
.add_event::<GamepadEvent>()
|
||||
.add_event::<RawGamepadEvent>()
|
||||
.add_event::<RawGamepadAxisChangedEvent>()
|
||||
.add_event::<RawGamepadButtonChangedEvent>()
|
||||
.add_event::<GamepadRumbleRequest>()
|
||||
.init_resource::<GamepadSettings>()
|
||||
.init_resource::<Gamepads>()
|
||||
.init_resource::<ButtonInput<GamepadButton>>()
|
||||
.init_resource::<Axis<GamepadAxis>>()
|
||||
.init_resource::<Axis<GamepadButton>>()
|
||||
.init_resource::<AccumulatedMouseMotion>()
|
||||
.init_resource::<AccumulatedMouseScroll>()
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
(
|
||||
gamepad_event_system,
|
||||
gamepad_connection_system.after(gamepad_event_system),
|
||||
gamepad_button_event_system
|
||||
.after(gamepad_event_system)
|
||||
.after(gamepad_connection_system),
|
||||
gamepad_axis_event_system
|
||||
.after(gamepad_event_system)
|
||||
.after(gamepad_connection_system),
|
||||
gamepad_connection_system,
|
||||
gamepad_event_processing_system.after(gamepad_connection_system),
|
||||
)
|
||||
.in_set(InputSystem),
|
||||
)
|
||||
|
@ -141,8 +131,15 @@ impl Plugin for InputPlugin {
|
|||
.register_type::<DoubleTapGesture>()
|
||||
.register_type::<PanGesture>()
|
||||
.register_type::<TouchInput>()
|
||||
.register_type::<GamepadEvent>()
|
||||
.register_type::<GamepadButtonInput>()
|
||||
.register_type::<RawGamepadEvent>()
|
||||
.register_type::<RawGamepadAxisChangedEvent>()
|
||||
.register_type::<RawGamepadButtonChangedEvent>()
|
||||
.register_type::<GamepadConnectionEvent>()
|
||||
.register_type::<GamepadButtonChangedEvent>()
|
||||
.register_type::<GamepadAxisChangedEvent>()
|
||||
.register_type::<GamepadButtonStateChangedEvent>()
|
||||
.register_type::<GamepadInfo>()
|
||||
.register_type::<GamepadConnection>()
|
||||
.register_type::<GamepadSettings>()
|
||||
.register_type::<AccumulatedMouseMotion>()
|
||||
.register_type::<AccumulatedMouseScroll>();
|
||||
|
|
|
@ -9,35 +9,22 @@ fn main() {
|
|||
.run();
|
||||
}
|
||||
|
||||
fn gamepad_system(
|
||||
gamepads: Res<Gamepads>,
|
||||
button_inputs: Res<ButtonInput<GamepadButton>>,
|
||||
button_axes: Res<Axis<GamepadButton>>,
|
||||
axes: Res<Axis<GamepadAxis>>,
|
||||
) {
|
||||
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);
|
||||
fn gamepad_system(gamepads: Query<(Entity, &Gamepad)>) {
|
||||
for (entity, gamepad) in &gamepads {
|
||||
if gamepad.just_pressed(GamepadButton::South) {
|
||||
info!("{:?} just pressed South", entity);
|
||||
} else if gamepad.just_released(GamepadButton::South) {
|
||||
info!("{:?} just released South", entity);
|
||||
}
|
||||
|
||||
let right_trigger = button_axes
|
||||
.get(GamepadButton::new(
|
||||
gamepad,
|
||||
GamepadButtonType::RightTrigger2,
|
||||
))
|
||||
.unwrap();
|
||||
let right_trigger = gamepad.get(GamepadButton::RightTrigger2).unwrap();
|
||||
if right_trigger.abs() > 0.01 {
|
||||
info!("{:?} RightTrigger2 value is {}", gamepad, right_trigger);
|
||||
info!("{:?} RightTrigger2 value is {}", entity, right_trigger);
|
||||
}
|
||||
|
||||
let left_stick_x = axes
|
||||
.get(GamepadAxis::new(gamepad, GamepadAxisType::LeftStickX))
|
||||
.unwrap();
|
||||
let left_stick_x = gamepad.get(GamepadAxis::LeftStickX).unwrap();
|
||||
if left_stick_x.abs() > 0.01 {
|
||||
info!("{:?} LeftStickX value is {}", gamepad, left_stick_x);
|
||||
info!("{:?} LeftStickX value is {}", entity, left_stick_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use bevy::{
|
||||
input::gamepad::{
|
||||
GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadButtonInput,
|
||||
GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadButtonStateChangedEvent,
|
||||
GamepadConnectionEvent, GamepadEvent,
|
||||
},
|
||||
prelude::*,
|
||||
|
@ -17,15 +17,14 @@ fn main() {
|
|||
|
||||
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, as measured
|
||||
// by `Axis<GamepadButton>`. Whenever that value changes, this event is emitted.
|
||||
// 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` and measured by
|
||||
// `Input<GamepadButton>`. When the threshold is crossed and the button state changes,
|
||||
// this event is emitted.
|
||||
mut button_input_events: EventReader<GamepadButtonInput>,
|
||||
// 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);
|
||||
|
@ -33,15 +32,13 @@ fn gamepad_events(
|
|||
for axis_changed_event in axis_changed_events.read() {
|
||||
info!(
|
||||
"{:?} of {:?} is changed to {}",
|
||||
axis_changed_event.axis_type, axis_changed_event.gamepad, axis_changed_event.value
|
||||
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_type,
|
||||
button_changed_event.gamepad,
|
||||
button_changed_event.value
|
||||
button_changed_event.button, button_changed_event.entity, button_changed_event.value
|
||||
);
|
||||
}
|
||||
for button_input_event in button_input_events.read() {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! pressed.
|
||||
|
||||
use bevy::{
|
||||
input::gamepad::{GamepadRumbleIntensity, GamepadRumbleRequest},
|
||||
input::gamepad::{Gamepad, GamepadRumbleIntensity, GamepadRumbleRequest},
|
||||
prelude::*,
|
||||
utils::Duration,
|
||||
};
|
||||
|
@ -15,51 +15,43 @@ fn main() {
|
|||
}
|
||||
|
||||
fn gamepad_system(
|
||||
gamepads: Res<Gamepads>,
|
||||
button_inputs: Res<ButtonInput<GamepadButton>>,
|
||||
gamepads: Query<(Entity, &Gamepad)>,
|
||||
mut rumble_requests: EventWriter<GamepadRumbleRequest>,
|
||||
) {
|
||||
for gamepad in gamepads.iter() {
|
||||
let button_pressed = |button| {
|
||||
button_inputs.just_pressed(GamepadButton {
|
||||
gamepad,
|
||||
button_type: button,
|
||||
})
|
||||
};
|
||||
|
||||
if button_pressed(GamepadButtonType::North) {
|
||||
for (entity, gamepad) in &gamepads {
|
||||
if gamepad.just_pressed(GamepadButton::North) {
|
||||
info!(
|
||||
"North face button: strong (low-frequency) with low intensity for rumble for 5 seconds. Press multiple times to increase intensity."
|
||||
);
|
||||
rumble_requests.send(GamepadRumbleRequest::Add {
|
||||
gamepad,
|
||||
gamepad: entity,
|
||||
intensity: GamepadRumbleIntensity::strong_motor(0.1),
|
||||
duration: Duration::from_secs(5),
|
||||
});
|
||||
}
|
||||
|
||||
if button_pressed(GamepadButtonType::East) {
|
||||
if gamepad.just_pressed(GamepadButton::East) {
|
||||
info!("East face button: maximum rumble on both motors for 5 seconds");
|
||||
rumble_requests.send(GamepadRumbleRequest::Add {
|
||||
gamepad,
|
||||
gamepad: entity,
|
||||
duration: Duration::from_secs(5),
|
||||
intensity: GamepadRumbleIntensity::MAX,
|
||||
});
|
||||
}
|
||||
|
||||
if button_pressed(GamepadButtonType::South) {
|
||||
if gamepad.just_pressed(GamepadButton::South) {
|
||||
info!("South face button: low-intensity rumble on the weak motor for 0.5 seconds");
|
||||
rumble_requests.send(GamepadRumbleRequest::Add {
|
||||
gamepad,
|
||||
gamepad: entity,
|
||||
duration: Duration::from_secs_f32(0.5),
|
||||
intensity: GamepadRumbleIntensity::weak_motor(0.25),
|
||||
});
|
||||
}
|
||||
|
||||
if button_pressed(GamepadButtonType::West) {
|
||||
if gamepad.just_pressed(GamepadButton::West) {
|
||||
info!("West face button: custom rumble intensity for 5 second");
|
||||
rumble_requests.send(GamepadRumbleRequest::Add {
|
||||
gamepad,
|
||||
gamepad: entity,
|
||||
intensity: GamepadRumbleIntensity {
|
||||
// intensity low-frequency motor, usually on the left-hand side
|
||||
strong_motor: 0.5,
|
||||
|
@ -70,9 +62,9 @@ fn gamepad_system(
|
|||
});
|
||||
}
|
||||
|
||||
if button_pressed(GamepadButtonType::Start) {
|
||||
if gamepad.just_pressed(GamepadButton::Start) {
|
||||
info!("Start button: Interrupt the current rumble");
|
||||
rumble_requests.send(GamepadRumbleRequest::Stop { gamepad });
|
||||
rumble_requests.send(GamepadRumbleRequest::Stop { gamepad: entity });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::f32::consts::PI;
|
||||
|
||||
use bevy::{
|
||||
input::gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadSettings},
|
||||
input::gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadConnectionEvent},
|
||||
prelude::*,
|
||||
sprite::{Anchor, MaterialMesh2dBundle, Mesh2dHandle},
|
||||
};
|
||||
|
@ -25,20 +25,20 @@ const LIVE_COLOR: Color = Color::srgb(0.4, 0.4, 0.4);
|
|||
const DEAD_COLOR: Color = Color::srgb(0.13, 0.13, 0.13);
|
||||
|
||||
#[derive(Component, Deref)]
|
||||
struct ReactTo(GamepadButtonType);
|
||||
struct ReactTo(GamepadButton);
|
||||
#[derive(Component)]
|
||||
struct MoveWithAxes {
|
||||
x_axis: GamepadAxisType,
|
||||
y_axis: GamepadAxisType,
|
||||
x_axis: GamepadAxis,
|
||||
y_axis: GamepadAxis,
|
||||
scale: f32,
|
||||
}
|
||||
#[derive(Component)]
|
||||
struct TextWithAxes {
|
||||
x_axis: GamepadAxisType,
|
||||
y_axis: GamepadAxisType,
|
||||
x_axis: GamepadAxis,
|
||||
y_axis: GamepadAxis,
|
||||
}
|
||||
#[derive(Component, Deref)]
|
||||
struct TextWithButtonValue(GamepadButtonType);
|
||||
struct TextWithButtonValue(GamepadButton);
|
||||
|
||||
#[derive(Component)]
|
||||
struct ConnectedGamepadsText;
|
||||
|
@ -84,7 +84,7 @@ struct GamepadButtonBundle {
|
|||
|
||||
impl GamepadButtonBundle {
|
||||
pub fn new(
|
||||
button_type: GamepadButtonType,
|
||||
button_type: GamepadButton,
|
||||
mesh: Mesh2dHandle,
|
||||
material: Handle<ColorMaterial>,
|
||||
x: f32,
|
||||
|
@ -140,28 +140,28 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::North,
|
||||
GamepadButton::North,
|
||||
meshes.circle.clone(),
|
||||
materials.normal.clone(),
|
||||
0.,
|
||||
BUTTON_CLUSTER_RADIUS,
|
||||
));
|
||||
parent.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::South,
|
||||
GamepadButton::South,
|
||||
meshes.circle.clone(),
|
||||
materials.normal.clone(),
|
||||
0.,
|
||||
-BUTTON_CLUSTER_RADIUS,
|
||||
));
|
||||
parent.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::West,
|
||||
GamepadButton::West,
|
||||
meshes.circle.clone(),
|
||||
materials.normal.clone(),
|
||||
-BUTTON_CLUSTER_RADIUS,
|
||||
0.,
|
||||
));
|
||||
parent.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::East,
|
||||
GamepadButton::East,
|
||||
meshes.circle.clone(),
|
||||
materials.normal.clone(),
|
||||
BUTTON_CLUSTER_RADIUS,
|
||||
|
@ -172,7 +172,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
// Start and Pause
|
||||
|
||||
commands.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::Select,
|
||||
GamepadButton::Select,
|
||||
meshes.start_pause.clone(),
|
||||
materials.normal.clone(),
|
||||
-30.,
|
||||
|
@ -180,7 +180,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
));
|
||||
|
||||
commands.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::Start,
|
||||
GamepadButton::Start,
|
||||
meshes.start_pause.clone(),
|
||||
materials.normal.clone(),
|
||||
30.,
|
||||
|
@ -196,7 +196,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::DPadUp,
|
||||
GamepadButton::DPadUp,
|
||||
meshes.triangle.clone(),
|
||||
materials.normal.clone(),
|
||||
0.,
|
||||
|
@ -204,7 +204,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
));
|
||||
parent.spawn(
|
||||
GamepadButtonBundle::new(
|
||||
GamepadButtonType::DPadDown,
|
||||
GamepadButton::DPadDown,
|
||||
meshes.triangle.clone(),
|
||||
materials.normal.clone(),
|
||||
0.,
|
||||
|
@ -214,7 +214,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
);
|
||||
parent.spawn(
|
||||
GamepadButtonBundle::new(
|
||||
GamepadButtonType::DPadLeft,
|
||||
GamepadButton::DPadLeft,
|
||||
meshes.triangle.clone(),
|
||||
materials.normal.clone(),
|
||||
-BUTTON_CLUSTER_RADIUS,
|
||||
|
@ -224,7 +224,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
);
|
||||
parent.spawn(
|
||||
GamepadButtonBundle::new(
|
||||
GamepadButtonType::DPadRight,
|
||||
GamepadButton::DPadRight,
|
||||
meshes.triangle.clone(),
|
||||
materials.normal.clone(),
|
||||
BUTTON_CLUSTER_RADIUS,
|
||||
|
@ -237,7 +237,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
// Triggers
|
||||
|
||||
commands.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::LeftTrigger,
|
||||
GamepadButton::LeftTrigger,
|
||||
meshes.trigger.clone(),
|
||||
materials.normal.clone(),
|
||||
-BUTTONS_X,
|
||||
|
@ -245,7 +245,7 @@ fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<Butto
|
|||
));
|
||||
|
||||
commands.spawn(GamepadButtonBundle::new(
|
||||
GamepadButtonType::RightTrigger,
|
||||
GamepadButton::RightTrigger,
|
||||
meshes.trigger.clone(),
|
||||
materials.normal.clone(),
|
||||
BUTTONS_X,
|
||||
|
@ -257,8 +257,10 @@ fn setup_sticks(
|
|||
mut commands: Commands,
|
||||
meshes: Res<ButtonMeshes>,
|
||||
materials: Res<ButtonMaterials>,
|
||||
gamepad_settings: Res<GamepadSettings>,
|
||||
) {
|
||||
// NOTE: This stops making sense because in entities because there isn't a "global" default,
|
||||
// instead each gamepad has its own default setting
|
||||
let gamepad_settings = GamepadSettings::default();
|
||||
let dead_upper =
|
||||
STICK_BOUNDS_SIZE * gamepad_settings.default_axis_settings.deadzone_upperbound();
|
||||
let dead_lower =
|
||||
|
@ -358,16 +360,16 @@ fn setup_sticks(
|
|||
spawn_stick(
|
||||
-STICKS_X,
|
||||
STICKS_Y,
|
||||
GamepadAxisType::LeftStickX,
|
||||
GamepadAxisType::LeftStickY,
|
||||
GamepadButtonType::LeftThumb,
|
||||
GamepadAxis::LeftStickX,
|
||||
GamepadAxis::LeftStickY,
|
||||
GamepadButton::LeftThumb,
|
||||
);
|
||||
spawn_stick(
|
||||
STICKS_X,
|
||||
STICKS_Y,
|
||||
GamepadAxisType::RightStickX,
|
||||
GamepadAxisType::RightStickY,
|
||||
GamepadButtonType::RightThumb,
|
||||
GamepadAxis::RightStickX,
|
||||
GamepadAxis::RightStickY,
|
||||
GamepadButton::RightThumb,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -403,16 +405,8 @@ fn setup_triggers(
|
|||
});
|
||||
};
|
||||
|
||||
spawn_trigger(
|
||||
-BUTTONS_X,
|
||||
BUTTONS_Y + 145.,
|
||||
GamepadButtonType::LeftTrigger2,
|
||||
);
|
||||
spawn_trigger(
|
||||
BUTTONS_X,
|
||||
BUTTONS_Y + 145.,
|
||||
GamepadButtonType::RightTrigger2,
|
||||
);
|
||||
spawn_trigger(-BUTTONS_X, BUTTONS_Y + 145., GamepadButton::LeftTrigger2);
|
||||
spawn_trigger(BUTTONS_X, BUTTONS_Y + 145., GamepadButton::RightTrigger2);
|
||||
}
|
||||
|
||||
fn setup_connected(mut commands: Commands) {
|
||||
|
@ -443,30 +437,28 @@ fn setup_connected(mut commands: Commands) {
|
|||
}
|
||||
|
||||
fn update_buttons(
|
||||
gamepads: Res<Gamepads>,
|
||||
button_inputs: Res<ButtonInput<GamepadButton>>,
|
||||
gamepads: Query<&Gamepad>,
|
||||
materials: Res<ButtonMaterials>,
|
||||
mut query: Query<(&mut Handle<ColorMaterial>, &ReactTo)>,
|
||||
) {
|
||||
for gamepad in gamepads.iter() {
|
||||
for buttons in &gamepads {
|
||||
for (mut handle, react_to) in query.iter_mut() {
|
||||
if button_inputs.just_pressed(GamepadButton::new(gamepad, **react_to)) {
|
||||
if buttons.just_pressed(**react_to) {
|
||||
*handle = materials.active.clone();
|
||||
}
|
||||
if button_inputs.just_released(GamepadButton::new(gamepad, **react_to)) {
|
||||
if buttons.just_released(**react_to) {
|
||||
*handle = materials.normal.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_button_values(
|
||||
mut events: EventReader<GamepadButtonChangedEvent>,
|
||||
mut query: Query<(&mut Text, &TextWithButtonValue)>,
|
||||
) {
|
||||
for button_event in events.read() {
|
||||
for (mut text, text_with_button_value) in query.iter_mut() {
|
||||
if button_event.button_type == **text_with_button_value {
|
||||
if button_event.button == **text_with_button_value {
|
||||
text.sections[0].value = format!("{:.3}", button_event.value);
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +471,7 @@ fn update_axes(
|
|||
mut text_query: Query<(&mut Text, &TextWithAxes)>,
|
||||
) {
|
||||
for axis_event in axis_events.read() {
|
||||
let axis_type = axis_event.axis_type;
|
||||
let axis_type = axis_event.axis;
|
||||
let value = axis_event.value;
|
||||
for (mut transform, move_with) in query.iter_mut() {
|
||||
if axis_type == move_with.x_axis {
|
||||
|
@ -501,18 +493,19 @@ fn update_axes(
|
|||
}
|
||||
|
||||
fn update_connected(
|
||||
gamepads: Res<Gamepads>,
|
||||
mut connected: EventReader<GamepadConnectionEvent>,
|
||||
gamepads: Query<(Entity, &Gamepad)>,
|
||||
mut query: Query<&mut Text, With<ConnectedGamepadsText>>,
|
||||
) {
|
||||
if !gamepads.is_changed() {
|
||||
if connected.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
connected.clear();
|
||||
let mut text = query.single_mut();
|
||||
|
||||
let formatted = gamepads
|
||||
.iter()
|
||||
.map(|g| format!("- {}", gamepads.name(g).unwrap()))
|
||||
.map(|(entity, gamepad)| format!("{} - {}", entity, gamepad.name()))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
|
|
Loading…
Reference in a new issue