Input engine brief overview
This is a brief overview of the parts I'll be using for input in our game engine. It's taken some time to research everything, and I've even already tried some things out only to find there are better ways to do it. Hopefully you'll find this info useful.
Configurable input
This is perhaps the most important feature. Always provide the ability to make your input configurable. There could be any number of reasons why players want to reconfigure the keys/buttons: Left-handed or one-handed players may want something entirely different, players could prefer ESDF instead of WASD, players might want to invert the Y-axis flight-stick controls, or any other number of reasons.
Keyboard input
Keyboard input is probably the most complex of all types of input, because we want to support more than just QWERTY keyboards. If you only need to support QWERTY, then this will be overkill for you, but then again, with a little knowledge of how things work, it's not hard to cater to other languages/keyboards and will make the majority of the world that much more happy when they start playing your game without having to configure anything on top of the defaults.
Position-based keys
Most games rely on the position of keys (WASD is a very common example for movement). The problem is there are many different keyboard layouts, and the WASD keys could be laid out differently. For example, on AZERTY, the coresponding key positions are ZQSD. When playing a game, a lot of times AZERTY users will press the ALT-Shift
key combo in Windows to switch the software layout to QWERTY (or they'll frustratingly roll their eyes when the Z or Q keys do something unexpected.) Obviously, it would be a pain and unrealistic to figure out what keyboard layout the player has and adjust your keys for them.
Fortunately, there's a way to specify keys by their position. They're called scancodes
.
Scancodes
Without going into too much detail, scancodes have a few different layouts themselves, called scancode sets
. Just know that Windows always maps to scancode set 1
. Other operating systems may use other sets, so if you need a good reference on the scancode key mappings, the chromium developers have already done this work for you. You can view their key/scancode mappings for the major operation systems in the file: /ui/events/keycodes/dom/dom_code_data.inc
So for example, the key at the same position as the w
key of a QWERTY keyboard in Windows will always have a scancode value of 0x0011
, even if it is a different character on another keyboard layout. The same key position on AZERTY is the z
key, but still has the same scancode value of 0x0011
.
For scancodes, for each key, the keyboard driver sends a separate code for the key-press event, called a make code
, and a separate code for the key-release event, called a break code
. The Windows APIs give us the make code, along with a separate way to tell if it was a key-press or key-release, so you don't have to keep track of the addtional break codes.
How do you get the scancode (technically the "make code") value? There are a couple ways in Windows. You can either use Windows KEYDOWN/UP Messages and/or the Raw Input API. The Raw Input API has one advantage: it can distinguish between multiple of the same device type (multiple keyboards, multiple mice, etc). This probably won't be necessary for your use-case though, so you might be fine just sticking with using Windows Messages. Keep in mind you can use both APIs together just fine, if you need to. Here's a post with more details and sample code.
Text input
Say you want to support a chat box in your game. Text input from the keyboard should be treated differently than looking at scancodes or KEYDOWN/UP messages. A single Unicode character outside of the English language can consist of up to 4 bytes of data, and in order for users to input the text, they most likely will need to type multiple keys to enter a single character in their language (sometimes using an IME). For this reason, you should probably use WM_CHAR messages, not scancodes nor KEYDOWN/UP messages, because WM_CHAR messages have already undergone some translation for you. If you're building your game for unicode, WM_CHAR will be UTF-16LE, and support surrogate-pairs. Some unicode characters (AKA code points
) will send 2 WM_CHAR messages to make up the entire character (via a surrogate-pair).
Related:
WM_CHAR documentation
Sample code to handle multiple WM_CHAR messages per character (surrogate pairs)
Graphical input
In some cases, you may want to support one language, or a subset of languages, for text entry using a bitmap font, such as this from Mother 3:
Mouse input
There are basically two types of mouse input: Unaltered high resolution input, and data that's been altered by an algorithm called Pointer Ballistics
.
Pointer Ballistics are what Windows users expect to feel when moving their mouse with a visible cursor. It allows the mouse to accellerate (or decelerate) movement of the cursor on the screen based on how fast you physically move your mouse, mouse resolution and screen resolution. Since this is what users expect to feel, Pointer Ballistics are important when displaying a cursor. This can be accomplished by using WM_MOUSEMOVE messages, in which you receive coordinates with the Pointer Ballistics already applied. If you're still curious, you can read about the Windows Pointer Ballistics algorithm.
The Raw Input API can be used to get unaltered high resolution mouse input, without Pointer Ballistics, but it has the advantage of using the full resolution of modern mice. This is preferred when you need more accuracy and you're not displaying a cursor, like in an FPS game. WM_MOUSEMOVE messages only fire on each "pixel", so they don't take advantage of high resolution mice.
Gamepad input
I still need to do more research in this area, but so far, it looks like I might be using at least two main APIs: Raw Input API and XInput. Basically, I want to be able to support a wide range of popular gamepads: Xbox, PlayStation, Nintendo Switch, 8BitDo, HID-compliant, others?
Tons of info and examples for Windows (github)
More to come
I still have a long way to go on implementing this, but I've done a good chunk of the leg-work. I'll update this page as I go.