The Ascent project had a bunch of upgrades these days, including better loading logic, a radar, pointer lock support and others. But the most important one certainly was decoupled input. I found it important enough to extract and polish it and create a separate GitHub repo for it. So, let me introduce you to decoupled-input!
Huh, what do you mean by that?
You often see event listeners for mouse and keyboard events scattered everywhere around the code. And controllers listening to specific key events. Or you see these listeners doing computation based on events outside of the loop. While this is perfectly doable, it has some major drawbacks: E.g. not being able to change key bindings during runtime, or just computations happening when the game is paused. This is what decoupled-input adresses.
The idea is simple (and not new or anything): You have an input controller that listens to user input events, does some normalizations and stores input data in a central place. In your render loop, you read user input from that place and react to it.
Benefits
There are quite some benefits that come along with using decoupled input:
It allows to change input bindings and devices without having to change the program logic; e.g. if the “fire” action was bound to the space key and it should later be bound to a mouse button, the change is only being made in the bindings configuration, and the game logic remains entirely unaffected.
Your code gets more readable: if(evt.keyCode == 68)
becomes if(input.boost)
.
Interpretation of input happens in the render loop, not in the event callback. This isn’t just aesthetics, it also means that there are no weird side effects, that you can profile your code’s performance more precisely, and that there is no computation happening if the game is paused.
Finally, boolean and analog axis input can be handled by the same code, which is a very sweet thing: You might want to allow acceleration to be triggered by a button, a key, or a gamepad axis.
Code
decoupled-input is here on GitHub. It comes with everything you need; the controller, the device handlers and some examples with example bindings. The project’s REDAME goes into detail about how it works, you might want to read it. Or, just check out the examples to see the code in action.
- Source: https://github.com/jensarps/decoupled-input
- README: https://github.com/jensarps/decoupled-input#readme
Examples:
-
First-Person style | code | bindings — This example uses
infiniteXAxis
- Car style | code | bindings
-
Fly style | code | bindings — This example uses
invertedYAxis
Feel free to leave opinions and comments here or on GitHub!