Refactor input to define hotkey interaction types#311
Closed
oznogon wants to merge 18 commits into
Closed
Conversation
Stripping the sign from keybind input values causes issues when two sides of a -1 to 1 joy/controller stick axis are each assigned to opposing actions, such as left/right, up/down, and forward/back controls. Retain the axis and apply the threshold only to the intermediate value.
discrete_step_size, threshold, repeat_delay, repeat_interval, and sensitivity were declared static, so all keybindings shared one value. Make them non-static instance members, with defaults matching the static initializers.
setValue() compares raw this->value against the threshold, but threshold_value is fabs(new_value). For negative axes, -0.8 < 0.5 is always true, so down_event fires every frame. Likewise, -0.8 >= 0.5 is never true, so up_event never fires. Store the absolute threshold_value so the next comparison is consistent.
Set the UI to show and commit a stale pending rebind.
loadKeybindings() checked entry.contains() for keys like "discrete", "repeat", "continuous", but saveKeybindings() writes "discrete_step", "repeat_delay", "continuous_sensitivity", etc. Per-binding properties were likewise saved but could never be loaded. Fix the keys.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
SP's input system passes input values down state, up state, and normalized values to a game, and the game defines how each hotkey interacts with game systems.
Before the move to SDL, SFML limitations indirectly delineated different interaction types (keyboards sent OS-defined repeating down events if enabled, buttons sent high-low values, axes sent variable values). After the move to SDL's more polished input system, keyboard repeat interactions were lost. However, players relied on this behavior.
SDL additionally differentiates between different controller and axis types in ways SFML did not. SDL can interpret and send values from both monodirectional gamecontroller axes (triggers, pressure-sensitive buttons) that normalize from 0 to 1 and rest at 0; bidirectional joysticks that normalize from -1 to 1 and rest at 0; and digital axes (gamepads, hats) that normalize across -1 to 1 like bidirectional axes but behave like buttons.
Redefine input to use these SDL features and allow hotkeys to define supported and default interactions. This allows hotkeys to decide whether to treat inputs as:
axis0), which rest at 0 as a minimum and don't return negative values; this represents triggers with an 0-1 axis, or half of a joystick's bidirectional axisaxis1, which rest at 0 as a center value between min (-1) and max (1) valuesThis PR provides an API for the game to use to define which interaction types a hotkey supports. For example, a hotkey representing an on-screen toggle button would support only discrete interactions, while combat manuevers would support continuous interactions (for accumulation via digital input), bidirectional axes (for strafe), and monodirectional axes (for boost).
This PR also provides a function to define which interaction type is its default, if multiple types are supported. This allows the game to simplify hotkey binding by defaulting to assigning inputs to the defined default type, while still providing flexibility for more advanced configurations that support unusual hardware, such as keyboard-emulating encoders; HOTAS/HOSAS monodirectional throttles, multi-axis sticks, digital and analog hats, pots/sliders, and encoder/pot knobs and dials; and MIDI-to-HID sliders, knobs, and pressure-sensitive buttons.
THIS SHOULD NOT CHANGE, AND DOES NOT REMOVE, EXISTING BEHAVIORS.
getDown,getUp, andgetValueremain in place. All hotkeys continue to work in EE with this PR as they would without it.Example implementations in EE
While initializing the
Keysclass, define a simple button-style hotkey as Discrete-only by invoking itssetSupportedInteractions(sp::io::Keybinding::Interaction::Discrete):void Keys::init() { pause.setLabel(tr("hotkey_menu", "General"), tr("hotkey_General", "Pause game")); + pause.setSupportedInteractions(sp::io::Keybinding::Interaction::Discrete); ...setSupportedInteractionstakes a mask, allowing multiple interactions to be supported at once:When a hotkey supports multiple interactions, define one as the default. If no default is explicitly defined, Continuous (representing the existing behavior without this PR) is assumed.
If a different interaction type should be the default for a different input type, optionally define that to override the global default interaction:
Helms slider behaviors, the most commonly debated action regarding the SFML/SDL changes in interactions:
The above:
Where the hotkey action is implemented in impulse controls, use the hotkey's new functions for receiving values for each supported interaction type.
isDiscreteStepDown()returns true if the hotkey is activatedisRepeatReady()returns true if the hotkey is activated and the timeout hasn't started, the timeout (i.e. 500ms) has elapsed, or the delay period (i.e. every 5ms after the timeout) has elapsedgetContinuousValue()returns 1.0 if the hotkey is activatedgetAxis0Value()andgetAxis1Value()return a normalized value