From e3787b5ccfd799b187a8e8e40d7e018f732822bb Mon Sep 17 00:00:00 2001 From: JaxonWasTaken Date: Fri, 31 Mar 2023 19:47:31 +0200 Subject: [PATCH] RMG-Input: implement "Analog Stick Sensitivity" --- Source/RMG-Core/Settings/Settings.cpp | 3 ++ Source/RMG-Core/Settings/SettingsID.hpp | 1 + .../Widget/ControllerImageWidget.cpp | 39 +++++++++++++------ .../Widget/ControllerImageWidget.hpp | 3 ++ .../UserInterface/Widget/ControllerWidget.cpp | 14 +++++++ .../UserInterface/Widget/ControllerWidget.hpp | 1 + .../UserInterface/Widget/ControllerWidget.ui | 37 ++++++++++++++++++ Source/RMG-Input/main.cpp | 20 ++++++---- 8 files changed, 99 insertions(+), 19 deletions(-) diff --git a/Source/RMG-Core/Settings/Settings.cpp b/Source/RMG-Core/Settings/Settings.cpp index 676a93a6..3874ee70 100644 --- a/Source/RMG-Core/Settings/Settings.cpp +++ b/Source/RMG-Core/Settings/Settings.cpp @@ -637,6 +637,9 @@ static l_Setting get_setting(SettingsID settingId) case SettingsID::Input_Deadzone: setting = {"", "Deadzone"}; break; + case SettingsID::Input_Sensitivity: + setting = {"", "Sensitivity"}; + break; case SettingsID::Input_Pak: setting = {"", "Pak"}; break; diff --git a/Source/RMG-Core/Settings/SettingsID.hpp b/Source/RMG-Core/Settings/SettingsID.hpp index 758aec1a..e38f2795 100644 --- a/Source/RMG-Core/Settings/SettingsID.hpp +++ b/Source/RMG-Core/Settings/SettingsID.hpp @@ -188,6 +188,7 @@ enum class SettingsID Input_DeviceName, Input_DeviceNum, Input_Deadzone, + Input_Sensitivity, Input_Pak, Input_GameboyRom, Input_GameboySave, diff --git a/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.cpp b/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.cpp index 1655f4be..6cd224fd 100644 --- a/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.cpp +++ b/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.cpp @@ -10,8 +10,8 @@ #include "ControllerImageWidget.hpp" #include +#include #include -#include #include #include @@ -62,6 +62,15 @@ void ControllerImageWidget::SetDeadzone(int value) } } +void ControllerImageWidget::SetSensitivity(int value) +{ + if (this->sensitivityValue != value) + { + this->sensitivityValue = value; + this->needImageUpdate = true; + } +} + void ControllerImageWidget::ClearControllerState() { // reset button state @@ -153,27 +162,35 @@ void ControllerImageWidget::paintEvent(QPaintEvent *event) renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatio); QRectF rectF = renderer.viewBoxF(); - double offsetx = 0, offsety = 0; const int width = rectF.width(); const int height = rectF.height(); // we'll move the analog stick by a percentage // of the total width/height from the image - const double maxOffsety = ((double)(height * 0.12265f) / 2); - const double maxOffsetx = maxOffsety; - const double dist = sqrt(pow(this->xAxisState, 2) + pow(this->yAxisState, 2)); + const double absoluteMaxOffset = ((double)(height * 0.12265f) / 2); + // slope as in line gradient + const double offsetSlope = absoluteMaxOffset / 100; + // take sensitivity into account + const double sensitivityFactor = this->sensitivityValue / 100.0; + const double sensitivityAdjustedMaxOffset = absoluteMaxOffset * std::min(sensitivityFactor, 1.0); + double offsetDeadzone = this->deadzoneValue * sensitivityFactor * offsetSlope; + int offsetx = this->xAxisState * sensitivityFactor * offsetSlope; + int offsety = this->yAxisState * sensitivityFactor * offsetSlope; + const double offsetDist = sqrt(pow(offsetx, 2) + pow(offsety, 2)); // take deadzone into account - if (dist > this->deadzoneValue) + // deadzone grows with sensitivity such that the deadzone + // is always a percentage of the real stick range + if (offsetDist <= offsetDeadzone) { - offsetx = (maxOffsetx / 100 * this->xAxisState); - offsety = (maxOffsety / 100 * this->yAxisState); + offsetx = 0; + offsety = 0; } // take circle range into account - if (dist > 100) + if (offsetDist > sensitivityAdjustedMaxOffset) { - offsetx = (maxOffsetx / 100 * (this->xAxisState / dist * 100)); - offsety = (maxOffsety / 100 * (this->yAxisState / dist * 100)); + offsetx = (offsetx / offsetDist) * sensitivityAdjustedMaxOffset; + offsety = (offsety / offsetDist) * sensitivityAdjustedMaxOffset; } // adjust rect with offset diff --git a/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.hpp b/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.hpp index 12e49783..af4772e7 100644 --- a/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.hpp +++ b/Source/RMG-Input/UserInterface/Widget/ControllerImageWidget.hpp @@ -31,6 +31,8 @@ private: int yAxisState = 0; // deadzone value, 0-100 int deadzoneValue = 0; + // sensitivity value, 25-175 + int sensitivityValue = 100; bool needImageUpdate = false; public: @@ -41,6 +43,7 @@ public: void SetXAxisState(int xAxis); void SetYAxisState(int yAxis); void SetDeadzone(int value); + void SetSensitivity(int value); void ClearControllerState(); void UpdateImage(); diff --git a/Source/RMG-Input/UserInterface/Widget/ControllerWidget.cpp b/Source/RMG-Input/UserInterface/Widget/ControllerWidget.cpp index 09adaa1d..c819a2cd 100644 --- a/Source/RMG-Input/UserInterface/Widget/ControllerWidget.cpp +++ b/Source/RMG-Input/UserInterface/Widget/ControllerWidget.cpp @@ -323,6 +323,7 @@ void ControllerWidget::setPluggedIn(bool value) this->analogStickDownButton, this->analogStickDownAddButton, this->analogStickDownRemoveButton, this->analogStickLeftButton, this->analogStickLeftAddButton, this->analogStickLeftRemoveButton, this->analogStickRightButton, this->analogStickRightAddButton, this->analogStickRightRemoveButton, + this->analogStickSensitivitySlider, // cbuttons this->cButtonsGroupBox, this->cbuttonUpButton, this->cbuttonUpAddButton, this->cbuttonUpRemoveButton, @@ -509,6 +510,16 @@ void ControllerWidget::on_deadZoneSlider_valueChanged(int value) this->controllerImageWidget->SetDeadzone(value); } +void ControllerWidget::on_analogStickSensitivitySlider_valueChanged(int value) +{ + QString title = tr("Analog Stick Sensitivity: "); + title += QString::number(value); + title += "%"; + + this->analogStickSensitivityGroupBox->setTitle(title); + this->controllerImageWidget->SetSensitivity(value); +} + void ControllerWidget::on_profileComboBox_currentIndexChanged(int value) { // save profile when switching profile @@ -1232,6 +1243,7 @@ void ControllerWidget::LoadSettings(QString sectionQString, bool loadUserProfile } } + this->analogStickSensitivitySlider->setValue(CoreSettingsGetIntValue(SettingsID::Input_Sensitivity, section)); this->deadZoneSlider->setValue(CoreSettingsGetIntValue(SettingsID::Input_Deadzone, section)); this->optionsDialogSettings.RemoveDuplicateMappings = CoreSettingsGetBoolValue(SettingsID::Input_RemoveDuplicateMappings, section); this->optionsDialogSettings.ControllerPak = CoreSettingsGetIntValue(SettingsID::Input_Pak, section); @@ -1309,6 +1321,7 @@ void ControllerWidget::SaveDefaultSettings() CoreSettingsSetValue(SettingsID::Input_DeviceName, section, std::string("None")); CoreSettingsSetValue(SettingsID::Input_DeviceNum, section, (int)InputDeviceType::None); CoreSettingsSetValue(SettingsID::Input_Deadzone, section, 9); + CoreSettingsSetValue(SettingsID::Input_Sensitivity, section, 100); CoreSettingsSetValue(SettingsID::Input_Pak, section, 0); CoreSettingsSetValue(SettingsID::Input_RemoveDuplicateMappings, section, true); CoreSettingsSetValue(SettingsID::Input_FilterEventsForButtons, section, true); @@ -1393,6 +1406,7 @@ void ControllerWidget::SaveSettings(QString section) CoreSettingsSetValue(SettingsID::Input_DeviceName, sectionStr, deviceName.toStdString()); CoreSettingsSetValue(SettingsID::Input_DeviceNum, sectionStr, deviceNum); CoreSettingsSetValue(SettingsID::Input_Deadzone, sectionStr, this->deadZoneSlider->value()); + CoreSettingsSetValue(SettingsID::Input_Sensitivity, sectionStr, this->analogStickSensitivitySlider->value()); CoreSettingsSetValue(SettingsID::Input_Pak, sectionStr, this->optionsDialogSettings.ControllerPak); CoreSettingsSetValue(SettingsID::Input_GameboyRom, sectionStr, this->optionsDialogSettings.GameboyRom); CoreSettingsSetValue(SettingsID::Input_GameboySave, sectionStr, this->optionsDialogSettings.GameboySave); diff --git a/Source/RMG-Input/UserInterface/Widget/ControllerWidget.hpp b/Source/RMG-Input/UserInterface/Widget/ControllerWidget.hpp index 53af17d7..836c79b4 100644 --- a/Source/RMG-Input/UserInterface/Widget/ControllerWidget.hpp +++ b/Source/RMG-Input/UserInterface/Widget/ControllerWidget.hpp @@ -138,6 +138,7 @@ public: private slots: void on_deadZoneSlider_valueChanged(int value); + void on_analogStickSensitivitySlider_valueChanged(int value); void on_profileComboBox_currentIndexChanged(int value); diff --git a/Source/RMG-Input/UserInterface/Widget/ControllerWidget.ui b/Source/RMG-Input/UserInterface/Widget/ControllerWidget.ui index 81b69941..3cb49d7d 100644 --- a/Source/RMG-Input/UserInterface/Widget/ControllerWidget.ui +++ b/Source/RMG-Input/UserInterface/Widget/ControllerWidget.ui @@ -601,6 +601,43 @@ + + + + Analog Stick Sensitivity: 100% + + + + + + + 0 + 0 + + + + 25 + + + 175 + + + 100 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 15 + + + + + + diff --git a/Source/RMG-Input/main.cpp b/Source/RMG-Input/main.cpp index 280ee5ed..7cbb25da 100644 --- a/Source/RMG-Input/main.cpp +++ b/Source/RMG-Input/main.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include // @@ -60,6 +60,7 @@ struct InputProfile { bool PluggedIn = false; int DeadzoneValue = 0; + int SensitivityValue = 100; N64ControllerPak ControllerPak = N64ControllerPak::None; @@ -217,6 +218,7 @@ static void load_settings(void) profile->PluggedIn = CoreSettingsGetBoolValue(SettingsID::Input_PluggedIn, section); profile->DeadzoneValue = CoreSettingsGetIntValue(SettingsID::Input_Deadzone, section); + profile->SensitivityValue = CoreSettingsGetIntValue(SettingsID::Input_Sensitivity, section); profile->ControllerPak = (N64ControllerPak)CoreSettingsGetIntValue(SettingsID::Input_Pak, section); profile->DeviceName = CoreSettingsGetStringValue(SettingsID::Input_DeviceName, section); profile->DeviceNum = CoreSettingsGetIntValue(SettingsID::Input_DeviceNum, section); @@ -537,16 +539,17 @@ static double simulate_deadzone(const double n64InputAxis, const double maxAxis, return axisAbsolute; } -// Credit: MerryMage -static void simulate_octagon(const double inputX, const double inputY, const double deadzoneFactor, int& outputX, int& outputY) +// Credit: MerryMage & fzurita +static void simulate_octagon(const double inputX, const double inputY, const double deadzoneFactor, const double sensitivityFactor, int& outputX, int& outputY) { - const double maxAxis = N64_AXIS_PEAK; - const double maxDiagonal = MAX_DIAGONAL_VALUE; - const int deadzone = (int)(deadzoneFactor * N64_AXIS_PEAK); + // don't increase emulated range at higher than 100% sensitivity + const double maxAxis = N64_AXIS_PEAK * std::min(sensitivityFactor, 1.0); + const double maxDiagonal = MAX_DIAGONAL_VALUE * std::min(sensitivityFactor, 1.0); + const int deadzone = (int)(deadzoneFactor * N64_AXIS_PEAK * sensitivityFactor); const double axisRange = maxAxis - deadzone; // scale to [-maxAxis, maxAxis] - double ax = inputX * maxAxis; - double ay = inputY * maxAxis; + double ax = std::min(inputX * sensitivityFactor, 1.0) * maxAxis; + double ay = std::min(inputY * sensitivityFactor, 1.0) * maxAxis; // check whether (ax, ay) is within the circle of radius MAX_AXIS double len = std::sqrt(ax*ax + ay*ay); @@ -879,6 +882,7 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys) inputX, // inputX inputY, // inputY profile->DeadzoneValue / 100.0, // deadzoneFactor + profile->SensitivityValue / 100.0, // sensitivityFactor octagonX, // outputX octagonY // outputY );