-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add getters and setters for InputAxis
and ButtonSettings
#3446
Closed
Closed
Changes from 6 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
9dda815
Add getters and setters for InputAxis
mfdorst 5cb6787
Make clippy happy
mfdorst 34da2f6
AxisSettings setters return the new value rather than panicking
mfdorst 038a094
Rename fields in AxisSettings
mfdorst 163785a
Validate button settings
mfdorst 71b735e
Add ButtonSettings::new()
mfdorst 573120c
Add AxisSettings::new()
mfdorst 4544e0a
Move ButtonSettings doc comment from impl to struct
mfdorst c5872ac
Add lots of backticks to doc comments
mfdorst 958118d
Cosmetic changes
mfdorst 1f44c99
Specify thiserror version as 1.0 rather than 1.0.30
mfdorst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,6 +2,18 @@ use crate::{Axis, Input}; | |||||
use bevy_app::{EventReader, EventWriter}; | ||||||
use bevy_ecs::system::{Res, ResMut}; | ||||||
use bevy_utils::{tracing::info, HashMap, HashSet}; | ||||||
use thiserror::Error; | ||||||
|
||||||
/// Errors that occur when setting settings for gamepad input | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
#[derive(Error, Debug)] | ||||||
pub enum GamepadSettingsError { | ||||||
#[error("{0}")] | ||||||
InvalidAxisSetting(String), | ||||||
#[error("{0}")] | ||||||
InvalidButtonSetting(String), | ||||||
} | ||||||
|
||||||
type Result<T, E = GamepadSettingsError> = std::result::Result<T, E>; | ||||||
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] | ||||||
|
@@ -131,70 +143,316 @@ impl GamepadSettings { | |||||
|
||||||
#[derive(Debug, Clone)] | ||||||
pub struct ButtonSettings { | ||||||
pub press: f32, | ||||||
pub release: f32, | ||||||
press_threshold: f32, | ||||||
release_threshold: f32, | ||||||
} | ||||||
|
||||||
impl Default for ButtonSettings { | ||||||
fn default() -> Self { | ||||||
ButtonSettings { | ||||||
press: 0.75, | ||||||
release: 0.65, | ||||||
press_threshold: 0.75, | ||||||
release_threshold: 0.65, | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
impl ButtonSettings { | ||||||
/// Creates a new ButtonSettings instance | ||||||
/// | ||||||
/// # Arguments | ||||||
/// | ||||||
/// + `press_threshold` - the value above which the button is considered pressed. | ||||||
/// + `release_threshold` - the value below which the button is considered released. | ||||||
/// | ||||||
/// Restrictions: | ||||||
/// + `0.0 <= release_threshold <= press_threshold <= 1.0` | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the restrictions are not met, InvalidButtonSetting will be returned. | ||||||
pub fn new(press_threshold: f32, release_threshold: f32) -> Result<ButtonSettings> { | ||||||
if 0.0 <= release_threshold | ||||||
&& release_threshold <= press_threshold | ||||||
&& press_threshold <= 1.0 | ||||||
{ | ||||||
Ok(ButtonSettings { | ||||||
press_threshold, | ||||||
release_threshold, | ||||||
}) | ||||||
} else { | ||||||
Err(GamepadSettingsError::InvalidButtonSetting( | ||||||
"The condition 0.0 <= release_threshold <= press_threshold <= 1.0 must hold true" | ||||||
.to_owned(), | ||||||
)) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Get the button input threshold above which the button is considered pressed | ||||||
pub fn press_threshold(&self) -> f32 { | ||||||
self.press_threshold | ||||||
} | ||||||
|
||||||
/// Try to set the button input threshold above which the button is considered pressed | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is outside the range [release_threshold, 1.0], InvalidButtonSetting is | ||||||
/// returned. | ||||||
pub fn try_set_press_threshold(&mut self, value: f32) -> Result<()> { | ||||||
if (self.release_threshold..=1.0).contains(&value) { | ||||||
self.press_threshold = value; | ||||||
Ok(()) | ||||||
} else { | ||||||
Err(GamepadSettingsError::InvalidButtonSetting( | ||||||
"press_threshold must be in the range [release_threshold, 1.0]".to_owned(), | ||||||
)) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the button input threshold above which the button is considered pressed. If the | ||||||
/// value passed is outside the range [release_threshold, 1.0], the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of press_threshold. | ||||||
pub fn set_press_threshold(&mut self, value: f32) -> f32 { | ||||||
self.try_set_press_threshold(value).ok(); | ||||||
self.press_threshold | ||||||
} | ||||||
|
||||||
/// Get the button input threshold below which the button is considered released | ||||||
pub fn release_threshold(&self) -> f32 { | ||||||
self.release_threshold | ||||||
} | ||||||
|
||||||
/// Try to set the button input threshold below which the button is considered released | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is outside the range [0.0, press_threshold], InvalidButtonSetting is | ||||||
/// returned. | ||||||
pub fn try_set_release_threshold(&mut self, value: f32) -> Result<()> { | ||||||
if (0.0..=self.press_threshold).contains(&value) { | ||||||
self.release_threshold = value; | ||||||
Ok(()) | ||||||
} else { | ||||||
Err(GamepadSettingsError::InvalidButtonSetting( | ||||||
"release_threshold must be in the range [0.0, press_threshold]".to_owned(), | ||||||
)) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the button input threshold below which the button is considered released. If the | ||||||
/// value passed is outside the range [0.0, press_threshold], the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of release_threshold. | ||||||
pub fn set_release_threshold(&mut self, value: f32) -> f32 { | ||||||
self.try_set_release_threshold(value).ok(); | ||||||
self.release_threshold | ||||||
} | ||||||
|
||||||
fn is_pressed(&self, value: f32) -> bool { | ||||||
value >= self.press | ||||||
value >= self.press_threshold | ||||||
} | ||||||
|
||||||
fn is_released(&self, value: f32) -> bool { | ||||||
value <= self.release | ||||||
value <= self.release_threshold | ||||||
} | ||||||
} | ||||||
|
||||||
/// Defines the sensitivity range and threshold for an axis. | ||||||
/// | ||||||
/// Values that are lower than `negative_high` will be rounded to -1.0. | ||||||
/// Values that are higher than `positive_high` will be rounded to 1.0. | ||||||
/// Values that are in-between `negative_low` and `positive_low` will be rounded to 0.0. | ||||||
/// Values that are lower than `livezone_lowerbound` will be rounded to -1.0. | ||||||
/// Values that are higher than `livezone_upperbound` will be rounded to 1.0. | ||||||
/// Values that are in-between `deadzone_lowerbound` and `deadzone_upperbound` will be rounded to 0.0. | ||||||
/// Otherwise, values will not be rounded. | ||||||
/// | ||||||
/// The valid range is from -1.0 to 1.0, inclusive. | ||||||
#[derive(Debug, Clone)] | ||||||
pub struct AxisSettings { | ||||||
pub positive_high: f32, | ||||||
pub positive_low: f32, | ||||||
pub negative_high: f32, | ||||||
pub negative_low: f32, | ||||||
livezone_upperbound: f32, | ||||||
deadzone_upperbound: f32, | ||||||
deadzone_lowerbound: f32, | ||||||
livezone_lowerbound: f32, | ||||||
///`threshold` defines the minimum difference between old and new values to apply the changes. | ||||||
pub threshold: f32, | ||||||
threshold: f32, | ||||||
} | ||||||
|
||||||
impl Default for AxisSettings { | ||||||
fn default() -> Self { | ||||||
AxisSettings { | ||||||
positive_high: 0.95, | ||||||
positive_low: 0.05, | ||||||
negative_high: -0.95, | ||||||
negative_low: -0.05, | ||||||
livezone_upperbound: 0.95, | ||||||
deadzone_upperbound: 0.05, | ||||||
livezone_lowerbound: -0.95, | ||||||
deadzone_lowerbound: -0.05, | ||||||
threshold: 0.01, | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
impl AxisSettings { | ||||||
fn filter(&self, new_value: f32, old_value: Option<f32>) -> Option<f32> { | ||||||
let new_value = if new_value <= self.positive_low && new_value >= self.negative_low { | ||||||
0.0 | ||||||
} else if new_value >= self.positive_high { | ||||||
1.0 | ||||||
} else if new_value <= self.negative_high { | ||||||
-1.0 | ||||||
/// Get the value above which inputs will be rounded up to 1.0 | ||||||
pub fn livezone_upperbound(&self) -> f32 { | ||||||
self.livezone_upperbound | ||||||
} | ||||||
|
||||||
/// Try to set the value above which inputs will be rounded up to 1.0 | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is less than deadzone_upperbound or greater than 1.0 | ||||||
pub fn try_set_livezone_upperbound(&mut self, value: f32) -> Result<()> { | ||||||
if value < self.deadzone_upperbound || value > 1.0 { | ||||||
Err(GamepadSettingsError::InvalidAxisSetting( | ||||||
"livezone_upperbound must be greater than deadzone_upperbound and less than 1.0" | ||||||
.to_owned(), | ||||||
)) | ||||||
} else { | ||||||
new_value | ||||||
}; | ||||||
self.livezone_upperbound = value; | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the value above which inputs will be rounded up to 1.0. If the value is less than | ||||||
/// deadzone_upperbound or greater than 1.0, the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of livezone_upperbound. | ||||||
pub fn set_livezone_upperbound(&mut self, value: f32) -> f32 { | ||||||
self.try_set_livezone_upperbound(value).ok(); | ||||||
self.livezone_upperbound | ||||||
} | ||||||
|
||||||
/// Get the value below which positive inputs will be rounded down to 0.0 | ||||||
pub fn deadzone_upperbound(&mut self) -> f32 { | ||||||
self.deadzone_upperbound | ||||||
} | ||||||
|
||||||
/// Try to set the value below which positive inputs will be rounded down to 0.0 | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is negative or greater than livezone_upperbound | ||||||
pub fn try_set_deadzone_upperbound(&mut self, value: f32) -> Result<()> { | ||||||
if value < 0.0 || value > self.livezone_upperbound { | ||||||
Err(GamepadSettingsError::InvalidAxisSetting( | ||||||
"deadzone_upperbound must be positive and less than livezone_upperbound".to_owned(), | ||||||
)) | ||||||
} else { | ||||||
self.deadzone_upperbound = value; | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the value below which positive inputs will be rounded down to 0.0. If the value | ||||||
/// passed is negative or greater than livezone_upperbound, the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of deadzone_upperbound. | ||||||
pub fn set_deadzone_upperbound(&mut self, value: f32) -> f32 { | ||||||
self.try_set_deadzone_upperbound(value).ok(); | ||||||
self.deadzone_upperbound | ||||||
} | ||||||
|
||||||
/// Get the value above which negative inputs will be rounded up to 0.0 | ||||||
pub fn deadzone_lowerbound(&self) -> f32 { | ||||||
self.deadzone_lowerbound | ||||||
} | ||||||
|
||||||
/// Try to set the value above which negative inputs will be rounded up to 0.0 | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is positive or less than livezone_lowerbound | ||||||
pub fn try_set_deadzone_lowerbound(&mut self, value: f32) -> Result<()> { | ||||||
if value < self.livezone_lowerbound || value > 0.0 { | ||||||
Err(GamepadSettingsError::InvalidAxisSetting( | ||||||
"deadzone_lowerbound must be negative and greater than livezone_lowerbound" | ||||||
.to_owned(), | ||||||
)) | ||||||
} else { | ||||||
self.deadzone_lowerbound = value; | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the value above which negative inputs will be rounded up to 0.0. If the value | ||||||
/// passed is positive or less than livezone_lowerbound, the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of deadzone_lowerbound. | ||||||
pub fn set_deadzone_lowerbound(&mut self, value: f32) -> f32 { | ||||||
self.try_set_deadzone_lowerbound(value).ok(); | ||||||
self.deadzone_lowerbound | ||||||
} | ||||||
|
||||||
/// Get the value below which inputs will be rounded down to -1.0 | ||||||
pub fn livezone_lowerbound(&self) -> f32 { | ||||||
self.livezone_lowerbound | ||||||
} | ||||||
|
||||||
/// Try to get the value below which inputs will be rounded down to -1.0 | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is less than -1.0 or greater than deadzone_lowerbound | ||||||
pub fn try_set_livezone_lowerbound(&mut self, value: f32) -> Result<()> { | ||||||
if value < -1.0 || value > self.deadzone_lowerbound { | ||||||
Err(GamepadSettingsError::InvalidAxisSetting( | ||||||
"livezone_lowerbound must be greater than -1.0 and less than deadzone_lowerbound" | ||||||
.to_owned(), | ||||||
)) | ||||||
} else { | ||||||
self.livezone_lowerbound = value; | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the value below which inputs will be rounded down to -1.0. If the value passed is | ||||||
/// less than -1.0 or greater than deadzone_lowerbound, the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of livezone_lowerbound. | ||||||
pub fn set_livezone_lowerbound(&mut self, value: f32) -> f32 { | ||||||
self.try_set_livezone_lowerbound(value).ok(); | ||||||
self.livezone_lowerbound | ||||||
} | ||||||
|
||||||
/// Get the minimum value by which input must change before the changes will be applied | ||||||
pub fn threshold(&self) -> f32 { | ||||||
self.threshold | ||||||
} | ||||||
|
||||||
/// Try to set the minimum value by which input must change before the changes will be applied | ||||||
/// | ||||||
/// # Errors | ||||||
/// | ||||||
/// If the value passed is not within [0.0, 2.0] | ||||||
pub fn try_set_threshold(&mut self, value: f32) -> Result<()> { | ||||||
if !(0.0..=2.0).contains(&value) { | ||||||
Err(GamepadSettingsError::InvalidAxisSetting( | ||||||
"threshold must be between 0.0 and 2.0, inclusive".to_owned(), | ||||||
)) | ||||||
} else { | ||||||
self.threshold = value; | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Try to set the minimum value by which input must change before the changes will be applied. | ||||||
/// If the value passed is not within [0.0, 2.0], the value will not be changed. | ||||||
/// | ||||||
/// Returns the new value of threshold. | ||||||
pub fn set_threshold(&mut self, value: f32) -> f32 { | ||||||
self.try_set_threshold(value).ok(); | ||||||
self.threshold | ||||||
} | ||||||
|
||||||
fn filter(&self, new_value: f32, old_value: Option<f32>) -> Option<f32> { | ||||||
let new_value = | ||||||
if new_value <= self.deadzone_upperbound && new_value >= self.deadzone_lowerbound { | ||||||
0.0 | ||||||
} else if new_value >= self.livezone_upperbound { | ||||||
1.0 | ||||||
} else if new_value <= self.livezone_lowerbound { | ||||||
-1.0 | ||||||
} else { | ||||||
new_value | ||||||
}; | ||||||
|
||||||
if let Some(old_value) = old_value { | ||||||
if (new_value - old_value).abs() <= self.threshold { | ||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe "configuring settings" or "setting up gamepad input". "setting settings" feels weird