diff options
| author | JMC47 <JMC4789@gmail.com> | 2019-01-10 19:04:27 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-01-10 19:04:27 -0500 |
| commit | 012338288e30e765e2ef8ec284cdbaceb42ba5ea (patch) | |
| tree | 2874ac5d304b6e881673cc7b5e256bc85d1df4d4 /Source/Core/InputCommon/ControllerEmu/ControlGroup | |
| parent | c2afcb0f6bba761c248031ee715a1db3fd4dbe05 (diff) | |
| parent | 7efa96eda979f5e717813e7c76cefb0720bc239a (diff) | |
Merge pull request #7662 from jordan-woyak/mapping-indicators-make-pretty
ControllerEmu: Make mapping indicators pretty
Diffstat (limited to 'Source/Core/InputCommon/ControllerEmu/ControlGroup')
9 files changed, 258 insertions, 233 deletions
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp index 9db29909fd..0cd5da6ba0 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp @@ -24,103 +24,41 @@ AnalogStick::AnalogStick(const char* const name_, std::unique_ptr<StickGate>&& s AnalogStick::AnalogStick(const char* const name_, const char* const ui_name_, std::unique_ptr<StickGate>&& stick_gate) - : ControlGroup(name_, ui_name_, GroupType::Stick), m_stick_gate(std::move(stick_gate)) + : ReshapableInput(name_, ui_name_, GroupType::Stick), m_stick_gate(std::move(stick_gate)) { for (auto& named_direction : named_directions) controls.emplace_back(std::make_unique<Input>(Translate, named_direction)); controls.emplace_back(std::make_unique<Input>(Translate, _trans("Modifier"))); - // Set default input radius to that of the gate radius (no resizing) - // Allow radius greater than 1.0 for definitions of rounded squares - // This is ideal for Xbox controllers (and probably others) - numeric_settings.emplace_back( - std::make_unique<NumericSetting>(_trans("Input Radius"), GetGateRadiusAtAngle(0.0), 0, 140)); - // Set default input shape to an octagon (no reshaping) - numeric_settings.emplace_back( - std::make_unique<NumericSetting>(_trans("Input Shape"), 0.0, 0, 50)); - numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Dead Zone"), 0, 0, 50)); + // Default input radius to that of the gate radius (no resizing) + // Default input shape to an octagon (no reshaping) + // Max deadzone to 50% + AddReshapingSettings(GetGateRadiusAtAngle(0.0), 0.0, 50); } -AnalogStick::StateData AnalogStick::GetState(bool adjusted) +AnalogStick::ReshapeData AnalogStick::GetReshapableState(bool adjusted) { - ControlState y = controls[0]->control_ref->State() - controls[1]->control_ref->State(); - ControlState x = controls[3]->control_ref->State() - controls[2]->control_ref->State(); + const ControlState y = controls[0]->control_ref->State() - controls[1]->control_ref->State(); + const ControlState x = controls[3]->control_ref->State() - controls[2]->control_ref->State(); // Return raw values. (used in UI) if (!adjusted) return {x, y}; - // TODO: make the AtAngle functions work with negative angles: - const ControlState ang = std::atan2(y, x) + MathUtil::TAU; - - const ControlState gate_max_dist = GetGateRadiusAtAngle(ang); - const ControlState input_max_dist = GetInputRadiusAtAngle(ang); - - // If input radius is zero we apply no scaling. - // This is useful when mapping native controllers without knowing intimate radius details. - const ControlState max_dist = input_max_dist ? input_max_dist : gate_max_dist; - - ControlState dist = std::sqrt(x * x + y * y) / max_dist; - - // If the modifier is pressed, scale the distance by the modifier's value. - // This is affected by the modifier's "range" setting which defaults to 50%. const ControlState modifier = controls[4]->control_ref->State(); - if (modifier) - { - // TODO: Modifier's range setting gets reset to 100% when the clear button is clicked. - // This causes the modifier to not behave how a user might suspect. - // Retaining the old scale-by-50% behavior until range is fixed to clear to 50%. - dist *= 0.5; - // dist *= modifier; - } - - // Apply deadzone as a percentage of the user-defined radius/shape: - const ControlState deadzone = GetDeadzoneRadiusAtAngle(ang); - dist = std::max(0.0, dist - deadzone) / (1.0 - deadzone); - - // Scale to the gate shape/radius: - dist = dist *= gate_max_dist; - - x = MathUtil::Clamp(std::cos(ang) * dist, -1.0, 1.0); - y = MathUtil::Clamp(std::sin(ang) * dist, -1.0, 1.0); - return {x, y}; -} -ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const -{ - return m_stick_gate->GetRadiusAtAngle(ang); -} - -ControlState AnalogStick::GetDeadzoneRadiusAtAngle(double ang) const -{ - return CalculateInputShapeRadiusAtAngle(ang) * numeric_settings[SETTING_DEADZONE]->GetValue(); + return Reshape(x, y, modifier); } -ControlState AnalogStick::GetInputRadiusAtAngle(double ang) const +AnalogStick::StateData AnalogStick::GetState() { - const ControlState radius = - CalculateInputShapeRadiusAtAngle(ang) * numeric_settings[SETTING_INPUT_RADIUS]->GetValue(); - // Clamp within the -1 to +1 square as input radius may be greater than 1.0: - return std::min(radius, SquareStickGate(1).GetRadiusAtAngle(ang)); + return GetReshapableState(true); } -ControlState AnalogStick::CalculateInputShapeRadiusAtAngle(double ang) const +ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const { - const auto shape = numeric_settings[SETTING_INPUT_SHAPE]->GetValue() * 4.0; - - if (shape < 1.0) - { - // Between 0 and 25 return a shape between octagon and circle - const auto amt = shape; - return OctagonStickGate(1).GetRadiusAtAngle(ang) * (1 - amt) + amt; - } - else - { - // Between 25 and 50 return a shape between circle and square - const auto amt = shape - 1.0; - return (1 - amt) + SquareStickGate(1).GetRadiusAtAngle(ang) * amt; - } + return m_stick_gate->GetRadiusAtAngle(ang); } OctagonAnalogStick::OctagonAnalogStick(const char* name, ControlState gate_radius) diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h index 2351189f74..cafb1dad51 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h @@ -10,35 +10,20 @@ namespace ControllerEmu { -class AnalogStick : public ControlGroup +class AnalogStick : public ReshapableInput { public: - enum - { - SETTING_INPUT_RADIUS, - SETTING_INPUT_SHAPE, - SETTING_DEADZONE, - }; - - struct StateData - { - ControlState x{}; - ControlState y{}; - }; + using StateData = ReshapeData; AnalogStick(const char* name, std::unique_ptr<StickGate>&& stick_gate); AnalogStick(const char* name, const char* ui_name, std::unique_ptr<StickGate>&& stick_gate); - StateData GetState(bool adjusted = true); + ReshapeData GetReshapableState(bool adjusted) final override; + ControlState GetGateRadiusAtAngle(double ang) const override; - // Angle is in radians and should be non-negative - ControlState GetGateRadiusAtAngle(double ang) const; - ControlState GetDeadzoneRadiusAtAngle(double ang) const; - ControlState GetInputRadiusAtAngle(double ang) const; + StateData GetState(); private: - ControlState CalculateInputShapeRadiusAtAngle(double ang) const; - std::unique_ptr<StickGate> m_stick_gate; }; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp index b0633ec6a5..0af6efc734 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp @@ -21,7 +21,8 @@ namespace ControllerEmu { -Cursor::Cursor(const std::string& name_) : ControlGroup(name_, GroupType::Cursor) +Cursor::Cursor(const std::string& name_) + : ReshapableInput(name_, name_, GroupType::Cursor), m_last_update(Clock::now()) { for (auto& named_direction : named_directions) controls.emplace_back(std::make_unique<Input>(Translate, named_direction)); @@ -31,89 +32,121 @@ Cursor::Cursor(const std::string& name_) : ControlGroup(name_, GroupType::Cursor controls.emplace_back(std::make_unique<Input>(Translate, _trans("Hide"))); controls.emplace_back(std::make_unique<Input>(Translate, _trans("Recenter"))); + // Default shape is a 1.0 square (no resizing/reshaping): + AddReshapingSettings(1.0, 0.5, 50); + numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Center"), 0.5)); numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Width"), 0.5)); numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Height"), 0.5)); - numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Dead Zone"), 0, 0, 20)); + boolean_settings.emplace_back(std::make_unique<BooleanSetting>(_trans("Relative Input"), false)); boolean_settings.emplace_back(std::make_unique<BooleanSetting>(_trans("Auto-Hide"), false)); } -Cursor::StateData Cursor::GetState(const bool adjusted) +Cursor::ReshapeData Cursor::GetReshapableState(bool adjusted) { - const ControlState zz = controls[4]->control_ref->State() - controls[5]->control_ref->State(); + const ControlState y = controls[0]->control_ref->State() - controls[1]->control_ref->State(); + const ControlState x = controls[3]->control_ref->State() - controls[2]->control_ref->State(); + + // Return raw values. (used in UI) + if (!adjusted) + return {x, y}; + + return Reshape(x, y, 0.0); +} - // silly being here - if (zz > m_state.z) - m_state.z = std::min(m_state.z + 0.1, zz); - else if (zz < m_state.z) - m_state.z = std::max(m_state.z - 0.1, zz); +ControlState Cursor::GetGateRadiusAtAngle(double ang) const +{ + // TODO: Change this to 0.5 and adjust the math, + // so pointer doesn't have to be clamped to the configured width/height? + return SquareStickGate(1.0).GetRadiusAtAngle(ang); +} - StateData result; - result.z = m_state.z; +Cursor::StateData Cursor::GetState(const bool adjusted) +{ + ControlState z = controls[4]->control_ref->State() - controls[5]->control_ref->State(); - if (m_autohide_timer > -1) + if (!adjusted) { - --m_autohide_timer; + const auto raw_input = GetReshapableState(false); + + return {raw_input.x, raw_input.y, z}; } - ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State(); - ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State(); + const auto input = GetReshapableState(true); - const ControlState deadzone = numeric_settings[3]->GetValue(); + // TODO: Using system time is ugly. + // Kill this after state is moved into wiimote rather than this class. + const auto now = Clock::now(); + const auto ms_since_update = + std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_update).count(); + m_last_update = now; - // reset auto-hide timer - if (std::abs(m_prev_xx - xx) > deadzone || std::abs(m_prev_yy - yy) > deadzone) - { - m_autohide_timer = TIMER_VALUE; - } + const double max_step = STEP_PER_SEC / 1000.0 * ms_since_update; + const double max_z_step = STEP_Z_PER_SEC / 1000.0 * ms_since_update; - // hide - const bool autohide = boolean_settings[1]->GetValue() && m_autohide_timer < 0; - if (controls[6]->control_ref->State() > 0.5 || autohide) - { - result.x = 10000; - result.y = 0; - } - else - { - // adjust cursor according to settings - if (adjusted) - { - xx *= (numeric_settings[1]->GetValue() * 2); - yy *= (numeric_settings[2]->GetValue() * 2); - yy += (numeric_settings[0]->GetValue() - 0.5); - } + // Apply deadzone to z: + const ControlState deadzone = numeric_settings[SETTING_DEADZONE]->GetValue(); + z = std::copysign(std::max(0.0, std::abs(z) - deadzone) / (1.0 - deadzone), z); + + // Smooth out z movement: + // FYI: Not using relative input for Z. + m_state.z += MathUtil::Clamp(z - m_state.z, -max_z_step, max_z_step); - // relative input - if (boolean_settings[0]->GetValue()) + // Relative input: + if (boolean_settings[0]->GetValue()) + { + // Recenter: + if (controls[7]->control_ref->State() > BUTTON_THRESHOLD) { - // deadzone to avoid the cursor slowly drifting - if (std::abs(xx) > deadzone) - m_state.x = MathUtil::Clamp(m_state.x + xx * SPEED_MULTIPLIER, -1.0, 1.0); - if (std::abs(yy) > deadzone) - m_state.y = MathUtil::Clamp(m_state.y + yy * SPEED_MULTIPLIER, -1.0, 1.0); - - // recenter - if (controls[7]->control_ref->State() > 0.5) - { - m_state.x = 0.0; - m_state.y = 0.0; - } + m_state.x = 0.0; + m_state.y = 0.0; } else { - m_state.x = xx; - m_state.y = yy; + m_state.x = MathUtil::Clamp(m_state.x + input.x * max_step, -1.0, 1.0); + m_state.y = MathUtil::Clamp(m_state.y + input.y * max_step, -1.0, 1.0); } + } + // Absolute input: + else + { + m_state.x = input.x; + m_state.y = input.y; + } + + StateData result = m_state; + + // Adjust cursor according to settings: + result.x *= (numeric_settings[SETTING_WIDTH]->GetValue() * 2); + result.y *= (numeric_settings[SETTING_HEIGHT]->GetValue() * 2); + result.y += (numeric_settings[SETTING_CENTER]->GetValue() - 0.5); + + const bool autohide = boolean_settings[1]->GetValue(); - result.x = m_state.x; - result.y = m_state.y; + // Auto-hide timer: + // TODO: should Z movement reset this? + if (!autohide || std::abs(m_prev_result.x - result.x) > AUTO_HIDE_DEADZONE || + std::abs(m_prev_result.y - result.y) > AUTO_HIDE_DEADZONE) + { + m_auto_hide_timer = AUTO_HIDE_MS; + } + else if (m_auto_hide_timer) + { + m_auto_hide_timer -= std::min<int>(ms_since_update, m_auto_hide_timer); } - m_prev_xx = xx; - m_prev_yy = yy; + m_prev_result = result; + + // If auto-hide time is up or hide button is held: + if (!m_auto_hide_timer || controls[6]->control_ref->State() > BUTTON_THRESHOLD) + { + // TODO: Use NaN or something: + result.x = 10000; + result.y = 0; + } return result; } + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h index b24cfdcba3..c66b2b896c 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h @@ -4,13 +4,15 @@ #pragma once +#include <chrono> #include <string> -#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" + +#include "InputCommon/ControllerEmu/StickGate.h" #include "InputCommon/ControllerInterface/Device.h" namespace ControllerEmu { -class Cursor : public ControlGroup +class Cursor : public ReshapableInput { public: struct StateData @@ -20,22 +22,42 @@ public: ControlState z{}; }; + enum + { + SETTING_CENTER = ReshapableInput::SETTING_COUNT, + SETTING_WIDTH, + SETTING_HEIGHT, + }; + explicit Cursor(const std::string& name); - StateData GetState(bool adjusted = false); + ReshapeData GetReshapableState(bool adjusted) final override; + ControlState GetGateRadiusAtAngle(double ang) const override; + + StateData GetState(bool adjusted); private: // This is used to reduce the cursor speed for relative input // to something that makes sense with the default range. - static constexpr double SPEED_MULTIPLIER = 0.04; + static constexpr double STEP_PER_SEC = 0.04 * 200; - // Sets the length for the auto-hide timer - static constexpr int TIMER_VALUE = 500; + // Smooth out forward/backward movements: + static constexpr double STEP_Z_PER_SEC = 0.05 * 200; + static constexpr int AUTO_HIDE_MS = 2500; + static constexpr double AUTO_HIDE_DEADZONE = 0.001; + + static constexpr double BUTTON_THRESHOLD = 0.5; + + // Not adjusted by width/height/center: StateData m_state; - int m_autohide_timer = TIMER_VALUE; - ControlState m_prev_xx; - ControlState m_prev_yy; + // Adjusted: + StateData m_prev_result; + + int m_auto_hide_timer = AUTO_HIDE_MS; + + using Clock = std::chrono::steady_clock; + Clock::time_point m_last_update; }; } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp index 8f26fde6fc..6ae4dfd668 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp @@ -4,6 +4,7 @@ #include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" +#include <algorithm> #include <cstddef> #include <memory> #include <string> @@ -21,23 +22,53 @@ MixedTriggers::MixedTriggers(const std::string& name_) : ControlGroup(name_, GroupType::MixedTriggers) { numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Threshold"), 0.9)); + numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Dead Zone"), 0.0, 0, 25)); } -void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlState* analog) +void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlState* analog, + bool adjusted) const { - const size_t trigger_count = controls.size() / 2; + const ControlState threshold = numeric_settings[SETTING_THRESHOLD]->GetValue(); + ControlState deadzone = numeric_settings[SETTING_DEADZONE]->GetValue(); - for (size_t i = 0; i < trigger_count; ++i, ++bitmasks, ++analog) + // Return raw values. (used in UI) + if (!adjusted) { - if (controls[i]->control_ref->State() > numeric_settings[0]->GetValue()) // threshold - { - *analog = 1.0; - *digital |= *bitmasks; - } - else + deadzone = 0.0; + } + + const int trigger_count = int(controls.size() / 2); + for (int i = 0; i != trigger_count; ++i) + { + ControlState button_value = controls[i]->control_ref->State(); + ControlState analog_value = controls[trigger_count + i]->control_ref->State(); + + // Apply deadzone: + analog_value = std::max(0.0, analog_value - deadzone) / (1.0 - deadzone); + button_value = std::max(0.0, button_value - deadzone) / (1.0 - deadzone); + + // Apply threshold: + if (button_value > threshold) { - *analog = controls[i + trigger_count]->control_ref->State(); + // Fully activate analog: + analog_value = 1.0; + + // Activate button: + *digital |= bitmasks[i]; } + + analog[i] = analog_value; } } + +ControlState MixedTriggers::GetDeadzone() const +{ + return numeric_settings[SETTING_DEADZONE]->GetValue(); +} + +ControlState MixedTriggers::GetThreshold() const +{ + return numeric_settings[SETTING_THRESHOLD]->GetValue(); +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h index cf85ffdc52..4e7b2adafe 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h @@ -15,6 +15,17 @@ class MixedTriggers : public ControlGroup public: explicit MixedTriggers(const std::string& name); - void GetState(u16* digital, const u16* bitmasks, ControlState* analog); + void GetState(u16* digital, const u16* bitmasks, ControlState* analog, + bool adjusted = true) const; + + ControlState GetDeadzone() const; + ControlState GetThreshold() const; + +private: + enum + { + SETTING_THRESHOLD, + SETTING_DEADZONE, + }; }; } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.cpp index 268d239f0b..d4e8bfa024 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.cpp @@ -22,7 +22,6 @@ namespace ControllerEmu ModifySettingsButton::ModifySettingsButton(std::string button_name) : Buttons(std::move(button_name)) { - numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Threshold"), 0.5)); } void ModifySettingsButton::AddInput(std::string button_name, bool toggle) diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.cpp index 9221b90243..2fec9c7bd0 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.cpp @@ -10,6 +10,8 @@ #include <string> #include "Common/Common.h" +#include "Common/MathUtil.h" + #include "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControllerEmu/Control/Control.h" #include "InputCommon/ControllerEmu/Control/Input.h" @@ -17,7 +19,8 @@ namespace ControllerEmu { -Tilt::Tilt(const std::string& name_) : ControlGroup(name_, GroupType::Tilt) +Tilt::Tilt(const std::string& name_) + : ReshapableInput(name_, name_, GroupType::Tilt), m_last_update(Clock::now()) { controls.emplace_back(std::make_unique<Input>(Translate, _trans("Forward"))); controls.emplace_back(std::make_unique<Input>(Translate, _trans("Backward"))); @@ -26,71 +29,63 @@ Tilt::Tilt(const std::string& name_) : ControlGroup(name_, GroupType::Tilt) controls.emplace_back(std::make_unique<Input>(Translate, _trans("Modifier"))); - numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Dead Zone"), 0, 0, 50)); - numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Circle Stick"), 0)); + // Set default input radius to the full 1.0 (no resizing) + // Set default input shape to a square (no reshaping) + // Max deadzone to 50% + AddReshapingSettings(1.0, 0.5, 50); + numeric_settings.emplace_back(std::make_unique<NumericSetting>(_trans("Angle"), 0.9, 0, 180)); } -Tilt::StateData Tilt::GetState(const bool step) +Tilt::ReshapeData Tilt::GetReshapableState(bool adjusted) { - // this is all a mess - - ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State(); - ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State(); - - ControlState deadzone = numeric_settings[0]->GetValue(); - ControlState circle = numeric_settings[1]->GetValue(); - auto const angle = numeric_settings[2]->GetValue() / 1.8; - ControlState m = controls[4]->control_ref->State(); - - // deadzone / circle stick code - // this section might be all wrong, but its working good enough, I think - - ControlState ang = atan2(yy, xx); - ControlState ang_sin = sin(ang); - ControlState ang_cos = cos(ang); + const ControlState y = controls[0]->control_ref->State() - controls[1]->control_ref->State(); + const ControlState x = controls[3]->control_ref->State() - controls[2]->control_ref->State(); - // the amt a full square stick would have at current angle - ControlState square_full = - std::min(ang_sin ? 1 / fabs(ang_sin) : 2, ang_cos ? 1 / fabs(ang_cos) : 2); + // Return raw values. (used in UI) + if (!adjusted) + return {x, y}; - // the amt a full stick would have that was (user setting circular) at current angle - // I think this is more like a pointed circle rather than a rounded square like it should be - ControlState stick_full = (square_full * (1 - circle)) + (circle); + const ControlState modifier = controls[4]->control_ref->State(); - ControlState dist = sqrt(xx * xx + yy * yy); + // Compute desired tilt: + StateData target = Reshape(x, y, modifier); - // dead zone code - dist = std::max(0.0, dist - deadzone * stick_full); - dist /= (1 - deadzone); + // Step the simulation. This is somewhat ugly being here. + // We should be able to GetState without changing state. + // State should be stored outside of this object inside the wiimote, + // and separately inside the UI. - // circle stick code - ControlState amt = dist / stick_full; - dist += (square_full - 1) * amt * circle; + // We're using system time rather than ticks to step this. + // I don't think that's too horrible as we can consider this part of user input. + // And at least the Mapping UI will behave sanely this way. + // TODO: when state is moved outside of this class have a separate Step() + // function that takes a ms_passed argument + const auto now = Clock::now(); + const auto ms_since_update = + std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_update).count(); + m_last_update = now; - if (m) - dist *= 0.5; + const double max_step = MAX_DEG_PER_SEC / 180.0 * ms_since_update / 1000; - yy = std::max(-1.0, std::min(1.0, ang_sin * dist)); - xx = std::max(-1.0, std::min(1.0, ang_cos * dist)); + // TODO: Allow wrap around from 1.0 to -1.0 + // (take the fastest route to target) - // this is kinda silly here - // gui being open will make this happen 2x as fast, o well + m_tilt.x += MathUtil::Clamp(target.x - m_tilt.x, -max_step, max_step); + m_tilt.y += MathUtil::Clamp(target.y - m_tilt.y, -max_step, max_step); - // silly - if (step) - { - if (xx > m_tilt.x) - m_tilt.x = std::min(m_tilt.x + 0.1, xx); - else if (xx < m_tilt.x) - m_tilt.x = std::max(m_tilt.x - 0.1, xx); + return m_tilt; +} - if (yy > m_tilt.y) - m_tilt.y = std::min(m_tilt.y + 0.1, yy); - else if (yy < m_tilt.y) - m_tilt.y = std::max(m_tilt.y - 0.1, yy); - } +Tilt::StateData Tilt::GetState() +{ + return GetReshapableState(true); +} - return {m_tilt.x * angle, m_tilt.y * angle}; +ControlState Tilt::GetGateRadiusAtAngle(double ang) const +{ + const ControlState max_tilt_angle = numeric_settings[SETTING_MAX_ANGLE]->GetValue() / 1.8; + return SquareStickGate(max_tilt_angle).GetRadiusAtAngle(ang); } + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.h index b03dc5a8b1..55afd43ba3 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Tilt.h @@ -4,26 +4,37 @@ #pragma once +#include <chrono> #include <string> -#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" + +#include "InputCommon/ControllerEmu/StickGate.h" #include "InputCommon/ControllerInterface/Device.h" namespace ControllerEmu { -class Tilt : public ControlGroup +class Tilt : public ReshapableInput { public: - struct StateData - { - ControlState x{}; - ControlState y{}; - }; + using StateData = ReshapeData; explicit Tilt(const std::string& name); - StateData GetState(bool step = true); + ReshapeData GetReshapableState(bool adjusted) final override; + ControlState GetGateRadiusAtAngle(double ang) const override; + + StateData GetState(); private: + enum + { + SETTING_MAX_ANGLE = ReshapableInput::SETTING_COUNT, + }; + + static constexpr int MAX_DEG_PER_SEC = 360 * 6; + StateData m_tilt; + + using Clock = std::chrono::steady_clock; + Clock::time_point m_last_update; }; } // namespace ControllerEmu |
