As for the piano, the faster her fingers flew over it, the more he marveled. She struck the keys with aplomb and ran
from one end of the keyboard to the other without a stop.
– G. Flaubert
In this tutorial, we will set up a basic input handler system to acquire input from keyboard devices.
Digital Input
In theory, checking whether a key is pressed or not, sounds absolutely easy. Even though Microsoft provides a function
to get the keyboard state or to query the state of a single key, there are, unfortunately, a few things to consider.
KeyCodes
Windows stores the state of each keyboard key in an array of 256 bytes. It would obviously be quite difficult to
remember which index corresponds to which key, therefore
so-called key codes, such as
VK_ESCAPE, for the, surprise, escape key, were introduced.
Quick Fingers
Now polling whether a key is pressed or not should be easy:
Everything seems straightforward, yes? Going back to the by now famous example of the student trying to walk across his
University campus without bumping into evil professors of mathematics, we could imagine him wanting to shoot
-balls (obviously with ) at the professors to hold their advance for a moment. Now just
imagine pressing the space button to throw such an -ball, what will happen? If the game is running at
fps, the space button would have to be pressed and released within 16ms, which is quite difficult actually, for the
action to be detected within a single frame, else, the student might throw several -balls on consecutive
frames with a single click. Similarly, holding the space bar will result in the student throwing a single
-ball per frame.
A quite simply but very effective method to solve this problem is to store the state of the keyboard during the previous
and the current frame, using two arrays of 256 bytes storing whether a key is down or not, leaving us with four possible
states for each key:
Previous Frame
Current Frame
State
false
false
Still Released
false
true
Just Pressed
true
false
Just Released
true
true
Still Pressed
Remember the problem of the player desiring to charge his weapon from the previous tutorial? Well, now he can easily do
so. Charging the missile would begin once the space key is Just Pressed, and charging continues as long as the space
key is Still Pressed, and the devastating missile will be fired upon the player’s enemies once the state of the space
bar is Just Released. Nice and easy!
To implement this system, a simple enum is enough:
As you can see, this structure simply links a keyboard key to one of the four possible states a key can be in, i.e. a
bind info could be that the escape key was just pressed.
Now an event can be mapped to one or multiple key bindings, for example, the event quit game could be mapped to the
key VK_ESCAPE with the state JustPressed, or the event toggle fps information could be mapped to the chord of
three keys: hold ‘control’ + ‘shift’ and press ‘F’. To do so, we introduce the GameCommand structure:
This simple structure stores a string defining the name of the command and a vector of key bindings, as described above.
I am sure you can see how it is not difficult any more, from here, to change the keys a command is mapped to. We will
come back to this in a later tutorial. For now, we focus on implementing the input handler class.
Input Mapping
Mapping keys to events is the job of the input handler class. A game must define all possible game actions, for example
in an enumeration:
The input handler then links those events to the above-mentioned commands:
Here is the C++-code for the above examples:
The Keyboard
With a configurable input handler in place, it is time to learn how to actually acquire keyboard input.
To store the keyboard state from the previous and the current frame, the input handler classes receive two arrays with
256 bytes and a method to fill those arrays with the data of each key:
Filling those arrays is done using
the GetAsyncKeyState that Microsoft
provides us:
If the function succeeds, the return value specifies whether the key is currently up or down. If the most significant
bit is set, the key is down. We can check for the most significant bit as follows:
And the method to actually fill the arrays is now straightforward:
We simply store the state from the previous frame and then read the state for the current frame using the isPressed
method on each key on the keyboard as defined above.
Polling
Polling is done, as explained in the previous tutorial, in the main game loop. All we have to do is to acquire the state
of the keyboard and to check for active key bindings. To do so, the input handler receives a second unordered map:
The reasoning behind this is basic: after each poll, the input handler runs through the map of all possible key
bindings and if it finds an active binding, it copies the address of the active command into the newly introduced map of
active commands. The game can then act on those commands later on.
To evaluate which key mappings are active, we make use of the table of possible key states defined at the start of this
tutorial as follows:
The update method now simply runs through the map of all possible key bindings and check whether their conditions are
fulfilled:
Note that for a chord to be active, the state of each mapped key must fit the specified key state.
Here is an example of how to use the input handler to acquire input and to react to it:
That’s it already.
You can download the source code for this
tutorial here. Please note
that in the meantime, I have made a few changes to the framework for the previous tutorials, but basically this new code
is what we had before, plus our new input handler in place.
To quit the application, press escape, to toggle the FPS information, hold control+shift and press F.
In the next tutorial, we will add mouse support to the input handler class.