GameDev Blog: Goblin Rules Football #18: Control Re-Mapping/Binding

I’ve mentioned before that I want to give players the ability to re-map/bind the controls for GRF. I created a control scheme that I think works for the game, but surely many other players will disagree! Assuming any other players ever play GRF, that is.

So, I went about trying to figure out how to allow players to change the controls. I found a lot of tutorials for how to do so, but the best one by far was from samyam and called “Complete and Persistent Control Rebinding with the New Input System – Unity Tutorial.” The tutorial uses the “Rebinding UI” sample from Unity’s Input System. I was originally using an older version of the Input System in my project, and the Rebind UI sample was only available in newer versions. But, I couldn’t update to the newer version of the Input System until I updated my Unity Editor version for the LTS version (2021.3.10f1). That in itself caused a lot of issues I had to fix, but eventually I was able to get it all working as it did before (I think. Still not 100% sure…).

To get the new Input System and the Rebind UI sample, go to the package manager and select and it under the “Packages Unity Registry.” The version of the Input System I am using is 1.4.2.

For the most part, I was able to follow along with samyam’s tutorial and get everything working in my project. After that was working, I wanted to make it a little bit “better” or really just “different.”

First, I modified the “RebindUIPrefab” object under the “Rebinding UI” sample folder so it would support TextMeshPro objects instead of just the regular “text” objects it was using. At first this didn’t work because I could reference TMPro. This was caused by an asmdef file, or assembly definition file, for the Rebinding UI sample. Removing the asmdef file resolved this for me. Alternatively, you can also just add your references manually to the asmdef file to look like this:

{
    "name": "Unity.InputSystem.RebindingUI",
    "references": [
        "GUID:75469ad4d38634e559750d17036d5f7c",
		"Unity.TextMeshPro"
    ],
    "optionalUnityReferences": [],
    "includePlatforms": [],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": false,
    "precompiledReferences": [],
    "autoReferenced": false,
    "defineConstraints": [],
    "versionDefines": []
}

Generally, it’s probably best to not just delete the asmdef files willy nilly. I did because I also ended up needing to reference other scripts in my project outside of the Rebinding UI sample, and the easiest way I could find to do that was to delete the definition file.

The main “change” I made, though, was how duplicate key bindings would be handled. In the samyam tutorial, they deal with duplicate bindings by simple rejecting the duplicate binding and prompting the player for another key. This works fine for the keyboard/mouse controls, but I had an issue for my gamepad controls. Every button on the gamepad is used in my control scheme. So, if a player tries to change the gamepad controls, they will never succeed because it will always be a duplicate.

I thought a simple solution would be to just swap the controls when a duplicate is entered. If a player wants to change the “attack” button from “X” to “B” on a gamepad, it would be a duplicate of the “block” action which is set to “B.” In this specific scenario it is pretty simple to swap the control bindings. “X” and “B” are the default bindings for the attack and block actions, respectively. You just apply the override binding to the attack action so it is now “B”, and then set an override to the block action to be “X” by referencing attack’s default binding of “X” which is stored in its InputBinding.path value.

An issue occurs though when you want to change bindings that already have overrides. Say that attack has been set to “Y” and block has been set to “A” as overrides. This means that the default paths of “X” and “B” have already been assigned to other actions. If you then try to set the attack action to “A,” block’s current binding, the process would be to set attack to “A” and block to “X,” attack’s default path. This results in block having a duplicate binding with whatever action attack was already swapped with. This occurs because the rebinding process is to set the override to attack before you can check for duplicates. I tried to figure out a way to store the previous override before applying the new override, but couldn’t figure it out. Instead, my process for duplicate bindings was the following:

  • First, check if duplicate binding’s default path is currently being used. If it is not, reset the duplicate binding to its default path
  • If the duplicate’s default path is already taken, check if the default path for the action you want to change (attack in the above sample) is available. If it is available, set the duplicate binding to the action’s default path (same as what was done before)
  • If the action’s default path is already taken, find what action is using that default path (the “second” duplicate). If the second duplicate’s default path is available, set the first duplicate’s override to the second’s default path
  • If that all fails, then you need to look deeper. Now, loop through every binding. Get the default path of each binding. Check if that default path is available. If it is, set the first duplicate binding to the first available default path

I imagine only using that last check would work for everything, but I figured I’d keep the first checks because that’s how I wanted to swapping to work mostly. I don’t know. Anyway you can find my modified RebindActionUI.cs script on github here.

You can also watch this very exciting video of the controls being changed using a gamepad on youtube!

This took me the better part of two weeks to complete, which is a long time for something I would assume a real gamedev could do in like an hour…but I’m pretty dumb and there were a lot of issues I created and had to resolve. It took me like 6 hours just to figure out how to move a scroll rect down as you move down through a list using arrow keys or a gamepad. Embarrassing…

Next Steps…

The next thing I think I will do is port the online/multiplayer to Steamworks.NET so everything works through that. Lobbies and stuff instead of just joining on IP. No port forwarding for Internet play. All that. Should be easy! (heh…)

After that, I have a few other minor things I want to add to the game. But, once I get Steamworks.NET working, I think I can consider updating that release date…

Smell ya later, nerds!