Our game has two input modes when using the controller. One is called Virtual Cursor, where it just mimics a mouse cursor directly. Moving the analog stick moves a cursor on screen. Pressing the gamepad bottom face button registers as EKeys::LeftMouseButton as far as the input system is concerned. This is done using a IInputProcessor.
The other is called Character Pilot, which is your typical 3rd-person controller movement setup. Analog stick moves the character, pressing the gamepad bottom face button registers as EKeys::Gamepad_FaceButton_Bottom. This also uses a IInputProcessor, but it's more of just a dummy one, it doesn't actually process inputs. It's mostly used to help figure out when the player has switched from mouse to controller at any given time.
Whenever the player stops piloting the character, we remove the Virtual Cursor processor and add the Character Pilot processor. Doing this changes what the gamepad bottom face button registers as:
With Virtual Cursor: EKeys::Gamepad_FaceButton_Bottom = EKeys::LeftMouseButton
With Chharacter Pilot: EKeys::Gamepad_FaceButton_Bottom unchanged
This is a pretty common thing we do in the game:
1. Player is piloting the character around.
2. Player presses bottom face button to interact with something.
3. Item pops up in the foreground for inspection. This is when we turn on the virtual cursor, so the player can click on stuff on the item.
4. Player presses bottom face button to close the foreground item. This is when we turn off the virtual cursor, so the player can continue piloting the character.
The issue we're having is that if the player holds the gamepad buttom face button down while this switch occurs, then releases a button afterwards, the button will stay in the "down" state in the key state map, because the EKeys enum that the button registers as has changed. So effectively we end up with a Gamepad_FaceButton_Bottom Pressed event, followed by a LeftMouseButton Release event, or the opposite, depending on which direction the switch is going. This results in a button getting stuck on, due to never seeing a release event because the actual release event changed EKeys values half-way thru.
The solutions:
1: Modify UPlayerInput to add a function to convert held LeftMouseButton keys into held Gamepad_FaceButton_Bottom keys, and vice versa. This sounds gross and bad, but would result in no gameplay code changes.
We would also need to hook this into the button remapping system, since it's not really "Gamepad_FaceButton_Bottom", it's "Input_LeftClick".
2: Replace all instances where we bind to some actor's "OnClicked" and use "OnReleased" instead. This will ensure that input processor swaps can only happen on button release, rather than button down.
This will take some time to implement, as I have no idea how prevelant using "OnClicked" is.
(Edit: turns out it was like 3 things that needed changing. It took 5 minutes, easy every time.)