This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
In this tutorial we will begin to write our Input manager. We will look at recording keyboard input (controller support will be added in a later tutorial) and how we can differentiate between a key that has just been pressed or released.
Create a new class called Input.
This summarises what we want from our input system. It wont build right now, as it does not know what a ‘Key’ is. So lets create that and also add an Update method.
The Key enum will hold every key we are interested in (currently thats not many but it will be expanded in future). We have specified a value for each entry as we will use these values as bit positions using the Bitmask class that we created in the previous tutorial.
The update method will be responsible for polling the keys and determining which keys have been pressed that frame, to do this it needs a way of storing the status of the keys; which is where our Bitmask class comes in.
We create two Bitmask variables: one to store the keys status this frame, and another to store the previous frames key status. We store two frames worth of data so that we can determine if a key was just released or pressed by checking its current status against last frames status (i’ll go into more detail on this shortly). We’ll update the bitmasks in the Update method:
1. First we set lastFramesKeys bits to what is currently stored in thisFramesKeys bits. This stores the previous frames keys status as we are about to change the contents of thisFramesKeys.
2. This will set or clear a bit depending on if the left key is being pressed. It calls the overloaded function ‘SetBit’ in our Bitmask class. This functions takes two parameters, the position of the bit and a boolean flag used to determine whether to set a bit to 1 or 0 (true or false). For the position parameter we use the keys enum int value (set when we created the enum); and for the boolean flag we call an SFML provided input method ‘isKeyPressed’; this will return true if the specified keys are pressed and therefore set our bit to 1 (key pressed) or 0 (key not pressed).
This is performed for each of they keys we are interested in. Here are a few examples of what our bitmask could look like after a typical update:
Left (pos 1) and Down (pos 4) keys pressed:
0000 0000 0000 0000 0000 0000 0000 1010
Up (pos 3) and Down (pos 4) keys pressed:
0000 0000 0000 0000 0000 0000 0000 1100
All keys pressed:
0000 0000 0000 0000 0000 0000 0011 1110
As you can see from the above examples, for each of our bitmasks (current and previous frames), we are only using 5 out of a possible 32 bits. In future this will not be too much of an issue as we expand the number of keys we are interested in, but it is definitely something to be aware of. We may want to create a reduced version of our bitmask or we may even find that we want to respond to more than 31 (32 minus the key::None option) different keys and create a larger bitmask. Also eventually, rather than always checking the status of all keys, we will want to add the ability to only check the keys we are currently interested in.
Now that we store the status of our keys, lets implement the methods for querying that data.
IsKeyPressed will recognise a pressed key as long as its held down, whereas the other two methods only work for one frame; so if the keys status is not queried that frame the state change will be missed.
Now that we have implemented the basics of our Input class; lets integrate the system in our engine.
We need to ensure our Input class is updated, we’ll do this by adding a new ‘CaptureInput’ method to our Game class.
We need to call this in our main game loop:
Now we are registering keyboard input we can move our viking. Lets change our Game’s Update method to do just that.
1. This is where we call our new input class. We call the IsKeyPressed method as we want to move the sprite as long as the specified key is pressed.
2. We need to scale our movement by deltaTime, for more information see my previous tutorial on frame-independent movement.
Now when you run the game, you will be able to move the sprite around the screen using either the arrow keys or WASD. You can also also change the input method calls to IsKeyDown (the character will only move once when a key is pressed) or IsKeyUp (the character will only move once when the key is released) to test that functionality.
As always, if you have any suggestions or are having problems with the code, then let me know in the comments and I’ll look into it. Thank you for reading 🙂