Skip to content

Commit

Permalink
Add threshold value for GamepadControlDirection, `MouseMoveDirect…
Browse files Browse the repository at this point in the history
…ion`, and `MouseScrollDirection` (#624)

* threshold

* minor

* address

* fix CI

* mention panic
  • Loading branch information
Shute052 authored Oct 6, 2024
1 parent d6ef9e1 commit a6949e8
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 43 deletions.
6 changes: 6 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release Notes

## Version 0.16.0 (Unreleased)

### Usability (0.16.0)

- added `threshold` value for `GamepadControlDirection`, `MouseMoveDirection`, and `MouseScrollDirection` to be considered pressed.

## Version 0.15.1

### Enhancements (0.15.1)
Expand Down
35 changes: 27 additions & 8 deletions src/axislike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@ impl AxisDirection {
}
}

/// Checks if the given `value` represents an active input in this direction.
/// Checks if the given `value` represents an active input in this direction,
/// considering the specified `threshold`.
///
/// # Requirements
///
/// - `threshold` >= `0.0`.
///
/// # Panics
///
/// Panics if the requirement isn't met.
#[must_use]
#[inline]
pub fn is_active(&self, value: f32) -> bool {
pub fn is_active(&self, value: f32, threshold: f32) -> bool {
assert!(threshold >= 0.0);
match self {
Self::Negative => value < 0.0,
Self::Positive => value > 0.0,
Self::Negative => value < -threshold,
Self::Positive => value > threshold,
}
}
}
Expand Down Expand Up @@ -151,11 +161,20 @@ impl DualAxisDirection {
}
}

/// Checks if the given `value` represents an active input in this direction.
/// Checks if the given `value` represents an active input in this direction,
/// considering the specified `threshold`.
///
/// # Requirements
///
/// - `threshold` >= `0.0`.
///
/// # Panics
///
/// Panics if the requirement isn't met.
#[must_use]
#[inline]
pub fn is_active(&self, value: Vec2) -> bool {
let component_along_axis = self.axis().get_value(value);
self.axis_direction().is_active(component_along_axis)
pub fn is_active(&self, value: Vec2, threshold: f32) -> bool {
let axis_value = self.axis().get_value(value);
self.axis_direction().is_active(axis_value, threshold)
}
}
57 changes: 48 additions & 9 deletions src/user_input/gamepad.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Gamepad inputs

use std::hash::{Hash, Hasher};

use bevy::input::gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadEvent};
use bevy::input::{Axis, ButtonInput};
use bevy::math::FloatOrd;
use bevy::prelude::{
Events, Gamepad, GamepadAxis, GamepadAxisType, GamepadButton, GamepadButtonType, Gamepads,
Reflect, Res, ResMut, Vec2, World,
Expand Down Expand Up @@ -69,29 +72,55 @@ fn read_axis_value(
/// app.update();
/// assert!(app.read_pressed(input));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
#[must_use]
pub struct GamepadControlDirection {
/// The axis that this input tracks.
pub axis: GamepadAxisType,

/// The direction of the axis to monitor (positive or negative).
pub side: AxisDirection,
pub direction: AxisDirection,

/// The threshold value for the direction to be considered pressed.
/// Must be non-negative.
pub threshold: f32,
}

impl GamepadControlDirection {
/// Creates a [`GamepadControlDirection`] triggered by a negative value on the specified `axis`.
#[inline]
pub const fn negative(axis: GamepadAxisType) -> Self {
let side = AxisDirection::Negative;
Self { axis, side }
Self {
axis,
direction: AxisDirection::Negative,
threshold: 0.0,
}
}

/// Creates a [`GamepadControlDirection`] triggered by a positive value on the specified `axis`.
#[inline]
pub const fn positive(axis: GamepadAxisType) -> Self {
let side = AxisDirection::Positive;
Self { axis, side }
Self {
axis,
direction: AxisDirection::Positive,
threshold: 0.0,
}
}

/// Sets the `threshold` value.
///
/// # Requirements
///
/// - `threshold` >= `0.0`.
///
/// # Panics
///
/// Panics if the requirement isn't met.
#[inline]
pub fn threshold(mut self, threshold: f32) -> Self {
assert!(threshold >= 0.0);
self.threshold = threshold;
self
}

/// "Up" on the left analog stick (positive Y-axis movement).
Expand Down Expand Up @@ -129,7 +158,7 @@ impl UserInput for GamepadControlDirection {
/// [`GamepadControlDirection`] represents a simple virtual button.
#[inline]
fn decompose(&self) -> BasicInputs {
BasicInputs::Simple(Box::new(*self))
BasicInputs::Simple(Box::new((*self).threshold(0.0)))
}
}

Expand All @@ -140,7 +169,7 @@ impl Buttonlike for GamepadControlDirection {
#[inline]
fn pressed(&self, input_store: &CentralInputStore, gamepad: Gamepad) -> bool {
let value = read_axis_value(input_store, gamepad, self.axis);
self.side.is_active(value)
self.direction.is_active(value, self.threshold)
}

/// Sends a [`GamepadEvent::Axis`] event with a magnitude of 1.0 for the specified direction on the provided [`Gamepad`].
Expand All @@ -150,7 +179,7 @@ impl Buttonlike for GamepadControlDirection {
let event = GamepadEvent::Axis(GamepadAxisChangedEvent {
gamepad,
axis_type: self.axis,
value: self.side.full_active_value(),
value: self.direction.full_active_value(),
});
world.resource_mut::<Events<GamepadEvent>>().send(event);
}
Expand All @@ -168,6 +197,16 @@ impl Buttonlike for GamepadControlDirection {
}
}

impl Eq for GamepadControlDirection {}

impl Hash for GamepadControlDirection {
fn hash<H: Hasher>(&self, state: &mut H) {
self.axis.hash(state);
self.direction.hash(state);
FloatOrd(self.threshold).hash(state);
}
}

impl UpdatableInput for GamepadAxis {
type SourceData = Axis<GamepadAxis>;

Expand Down
Loading

0 comments on commit a6949e8

Please sign in to comment.