Keyboard Input
The wonder of interactivity lies in being able to control your game with a keyboard or other types of hardware input. Without the ability to control your game, you are just staring at a looping animation. The "interface" between the human playing the game and the game responding to the human is the hardware such as a keyboard or mouse.
Hardware for interacting with games can come in many forms. The most common types of input hardware are a keyboard, mouse, or game controller. The type of hardware you use to interact with a game though depends on the type of game and user preferences.
In addition, this guide will show you how to use the keyboard to control your game. The options for how to respond to keyboard input are endless. Examples of what you can do with the keyboard are character movement, pausing the game, and exiting the game when necessary.
Go to the
guide project to see the source code for a fully working example of this guide.Step 1: Project setup
This tutorial will demonstrate how to use keyboard input to move and resize the game window in a Velaptor application. While this may seem simple, it is a crucial skill for any game developer to master.
To get started, create a new Velaptor application.
For more info, refer to the Project Setup guide.
The only game loop method required for this tutorial will be the OnUpdate()
method, which we
will be used to update the game state and resize and move the window respectively.
Step 2: Creating game state
We need to create some class fields to hold onto the state of the game. Let's do this now.
2.1: Creating the class fields
Create some class fields that will be useful for things such as getting keyboard state, saving keyboard state, and some variables for holding information for moving the window.
public class Game : Window
{
private const float VelocityXAndY = 300f;
private readonly IAppInput<KeyboardState> keyboard;
private KeyboardState currKeyState;
private KeyboardState prevKeyState;
private Vector2 velocity = Vector2.Zero;
}
2.2: Creating a keyboard object
To enable keyboard input in your Velaptor game, you'll need to create a new instance of IAppInput<KeyboardState>
,
which will be set to the class field keyboard
. This will provide access to the current state of the keyboard.
You can create a new keyboard object quickly and easily using Velaptor's HardwareFactory
class.
public Game() => this.keyboard = HardwareFactory.GetKeyboard();
The Factory pattern is a creational design pattern that provides an interface for creating objects. Click
for a great site about creational patterns.Step 3: Creating helpful methods
We don't NEED these methods that we are about to create. However, they will make our code more readable and easier to understand. This is good practice no matter what kind of software you are writing.
3.1: Key pressed detection
Create a private method in the Game
class with the name IsPressed()
which will check if a keyboard key has been pressed.
private bool IsPressed(KeyCode key) => this.currKeyState.IsKeyUp(key) && this.prevKeyState.IsKeyDown(key);
This method will check if the current state of the key is in the up position, and if the state of the same key was in the down position in the previous frame. If this is the case, then the key was pressed down and then released which means the key was pressed.
This will make more sense when we create our Update()
method logic.
3.2: Moving the window
When we press the arrow keys on the keyboard, we want to move the window around. To do this, create the MoveWindow()
below with the following code.
private void MoveWindow(FrameTime frameTime)
{
if (this.currKeyState.IsKeyDown(KeyCode.Right))
{
this.velocity.X = VelocityXAndY;
}
if (this.currKeyState.IsKeyDown(KeyCode.Left))
{
this.velocity.X = -VelocityXAndY; // Negate the velocity to move left
}
if (this.currKeyState.IsKeyDown(KeyCode.Up))
{
this.velocity.Y = -VelocityXAndY; // Negate the velocity to move up
}
if (this.currKeyState.IsKeyDown(KeyCode.Down))
{
this.velocity.Y = VelocityXAndY;
}
var displacement = this.velocity * (float)frameTime.ElapsedTime.TotalSeconds;
Position += displacement;
}
This method will check if an arrow key is in the down position. If it is, it will then set the velocity of the velocity
field component to a value that will move the window in the direction described by the arrow key.
This will be called later in our OnUpdate()
method.
Details
Expand me for more info!!
In our code, we're multiplying the velocity of the window's movement by the elapsed time since the last frame. This ensures that the window moves at a consistent speed, regardless of the frame rate. Because the CPU is responsible for executing the game logic and rendering each frame, a faster CPU can complete more operations in the same amount of time, resulting in a higher frame rate.To ensure that players have a consistent experience, game objects must move at the same speed on all computers, regardless of the CPU speed. This means that game developers must account for differences in hardware and optimize their code to achieve consistent results.
3.3: Changing the window size
The window size will be changed when we press the arrow keys. Create the ChangeWinSize()
method with the code below
to change the size of the window using the keyboard.
private void ChangeWinSize()
{
if (IsPressed(KeyCode.Right))
{
Width += 50; // Increase the width of the window
}
if (IsPressed(KeyCode.Left))
{
Width -= 50; // Decrease the width of the window
}
if (IsPressed(KeyCode.Up))
{
Height -= 50; // Decrease the height of the window
}
if (IsPressed(KeyCode.Down))
{
Height += 50; // Increase the height of the window
}
}
This method checks if the arrow keys have been pressed fully down and then released. If they have, then the size of
the window will be changed. This will also be called later in our OnUpdate()
method.
Step 4: Finish up
We can now finish up the rest by tracking the state of the keyboard and using some simple logic to move or resize the window based on the state of the keyboard keys.
4.1: Getting the keyboard state
To check the state of keyboard keys in your game, you need to retrieve the keyboard state for each frame of the game loop. This will allow you to analyze which keys are currently in the up or down position. With this information, you can make decisions about what actions to take based on the state of a single key or a combination of keys.
First, set the current and previous state of the keyboard.
protected override void OnUpdate(FrameTime frameTime)
{
this.currKeyState = this.keyboard.GetState();
this.prevKeyState = this.currKeyState;
}
To accurately detect keyboard input, we need to get and save both the current and previous states. This helps us track changes in key states, such as when a key is pressed down and then released, which is necessary for accurate detection of a full key press.
4.2: Putting it all together
In between the current and previous keyboard state lines of code, add the logic to the OnUpdate()
method below:
protected override void OnUpdate(FrameTime frameTime)
{
...
// If the left or right control key is pressed, move the window.
if (this.currKeyState.AnyCtrlKeysDown())
{
MoveWindow(frameTime);
}
else
{
// Change the window size.
ChangeWinSize();
}
this.velocity = Vector2.Zero;
...
}
The OnUpdate()
method will first check if the current state of any Ctrl keys is being pressed in the down position.
If this is the case, then the window will be moved. If not, then the window size will be changed.
4.3: Run it
Run the application and then begin by pressing the arrow keys to observe the window's width and height increase or decrease. The window will only resize when you release the arrow key from the down position.
Next, hold down any Ctrl key and then use the arrow keys to move the window around the screen. You'll notice that you can keep the window moving without releasing the arrow keys, as we're not looking for a full key press.
Now you have all that you need to get input from the user via the keyboard!!
Step 5: Bonus
It would be nice to see the current size and position of the window. To do this, we can override the OnResize()
method
and update the window's title with the new size and position.
Add the method below in the Game
class.
protected override void OnResize(SizeU size)
{
Title = $"Window Size: W: {Width} H: {Height} | Window Position: X: {Position.X} Y: {Position.Y}";
base.OnResize(size);
}
Run the application and now you can see the size and position of the window in the title bar.