How To Add Keyboard Controls To Your HTML5 Game
Despite the long history, keyboard control is probably still the best form of interaction with many genres of games.

Pre-requisites

Setup
Draw Images

Foreword

The tutorials here in general make a basic assumption that you are not a total newbie to programming or basic html. The walkthrough of the codes will zoom in and highlight areas that are crucial to creating the game, but expects some basic understanding of html as well as how to code in Javascript.

Also, in the name of clarity and ease of explanation, optimization of codes is not done.

Getting Started

We will be using a very basic html here with a game canvas object. It links to the javascript file called game.js which will contain some codes to write onto the canvas. It also links to the javascript file constants.js which will contain some static values to initialise the game. For completeness sake, this is how the html file looks like.

edited html

If you have gone through the earlier Set Up tutorial, you'll realise that it is the same. In fact, for subsequent tutorials, we will not be making much modifications to this html file.

For this tutorial, we will be using the same spritesheet that we used in the previous tutorial. Your files should be organised as below.

file structure

The img folder contains a single file, which is the sprite sheet. This sprite sheet contains 12 poses of a girl, and the background is transparent. We will not be going in depth to the codes below which relate to preloading the spritesheet.

//---------------
//Preloading ...
//---------------
//Preload Art Assets
// - Sprite Sheet
var charImage = new Image();
charImage.ready = false;
charImage.onload = setAssetReady;
charImage.src = PATH_CHAR;

function setAssetReady()
{
	this.ready = true;
}

//Display Preloading
ctx.fillRect(0,0,stage.width,stage.height);
ctx.fillStyle = "#000";
ctx.fillText(TEXT_PRELOADING, TEXT_PRELOADING_X, TEXT_PRELOADING_Y);
var preloader = setInterval(preloading, TIME_PER_FRAME);

That has been explained in the previous tutorial.

Step 1 - Adding a Keyboard Event Listener

Once the image has been preloaded, we proceed to clear the setInterval method that was doing the preloading.

function preloading()
{	
	if (charImage.ready)
	{
		clearInterval(preloader);
		
		//Initialise game
		facing = "E"; //N = North, E = East, S = South, W = West
		isMoving = false;
		
		gameloop = setInterval(update, TIME_PER_FRAME);			
		document.addEventListener("keydown",keyDownHandler, false);	
		document.addEventListener("keyup",keyUpHandler, false);	
	}
}

In line 41, we set the initial facing of the girl character to be East. To simplify the values, we will just use the initial letter, E. The variable isMoving will be used to track whether the girl is moving or not. Since the game just started, this variable will naturally be false.

Lines 45 and 46 are what causes your game to understand whether the player has pressed any keys. Line 45 adds an event listener to the document (your entire html file). The first parameter is the name of the event that it is listening to, in this case, the "keydown" event. The second parameter is a reference to the function that will be called when that event happens. You can ignore the third parameter for now as it simply states whether or not to capture the event in the capturing phase.

Overall, what line 45 is doing is, when a key is pressed, execute the keyDownHandler function.

Line 46 does the opposite, i.e. when a key is released, execute the keyUpHandler function.

Step 2 - Setting the Key Handler Functions

What we did earlier merely instructs the game system what functions to call when any particular event happens. Let us take a look at the keyDownHandler function first.

//------------
//Key Handlers
//------------
function keyDownHandler(event)
{
	var keyPressed = String.fromCharCode(event.keyCode);

	if (keyPressed == "W")
	{		
		facing = "N";
		isMoving = true;
	}
	else if (keyPressed == "D")
	{	
		facing = "E";
		isMoving = true;		
	}
	else if (keyPressed == "S")
	{	
		facing = "S";
		isMoving = true;		
	}
	else if (keyPressed == "A")
	{	
		facing = "W";
		isMoving = true;		
	}
}

Line 55 gets the event.keyCode which is generated whenever a key is pressed. It comes in a raw number form, which really isn't very helpful to us. The function String.fromCharCode(...) will take that number, and convert it into a meaningful form to us. A number 65, for example, refers to the key "A". You can refer to ASCII codes for more information on this, but for the purpose of doing these simple games, you don't really need to know further than this.

Lines 57 to 61 handle what happens when the player presses down on the W key. Since we are using the traditional WASD control scheme, the W key represents the player's intention to move the game character up, which is northwards. Hence, we set facing to be "N". isMoving is a variable which we use to track whether to animate the character to move, and since the player has pressed down the W key, isMoving should be set to true.

Now, let us see what happens when the player lifts up the W key.

function keyUpHandler(event)
{
	var keyPressed = String.fromCharCode(event.keyCode);
	
	if ((keyPressed == "W") || (keyPressed == "A") || 
		(keyPressed == "S") || (keyPressed == "D"))
	{
		isMoving = false;
	}
}

There is no need to change the facing of the game character when the player lifts up the key. What we just need to do, is to change the isMoving variable to false so that the character is no longer animating.

Step 3 - Animating the Sprite

The last step is really just to move the game character's position, and animate it accordingly.

if (isMoving)
	{
		if (facing == "N")
		{
			charY -= CHAR_SPEED;
			currY = IMAGE_START_NORTH_Y;
		}
		else if (facing == "E")
		{
			charX += CHAR_SPEED;
			currY = IMAGE_START_EAST_Y;
		}
		else if (facing == "S")
		{
			charY += CHAR_SPEED;
			currY = IMAGE_START_SOUTH_Y;
		}
		else if (facing == "W")
		{
			charX -= CHAR_SPEED;
			currY = IMAGE_START_WEST_Y;
		}
		
		currX += CHAR_WIDTH;
		
		if (currX >= SPRITE_WIDTH)
			currX = 0;
	}
	
	//Draw Image
	ctx.drawImage(charImage,currX,currY,CHAR_WIDTH,CHAR_HEIGHT,
					charX,charY,CHAR_WIDTH,CHAR_HEIGHT);

Under the update function, you can see that we first check if the isMoving variable is set to true. Remember that this variable will only be true at the instant where the player presses down on any of the WASD keys, and when the player keeps holding it down. The rest of the codes here capture the 4 different scenarios of movement in the 4 directions. Line 107 handles the situation where the W key is pressed, hence facing is "N". Line 109 thus follows to decrease the variable charY by CHAR_SPEED. It is important to note that charX and charY represents the game character's x and y positions respectively. So what that line is doing is, when the W key is pressed, the position of the game character moves up by CHAR_SPEED, which is a constant speed value.

game character movement

Also remember that in computer graphics, upwards is a negative y direction, and eastwards is the positive x direction.

Next, we see that the currY value has changed as well. Again, it is worth noting that currX and currY represents the coordinates of the image we are extracting from the spritesheet. When the player presses the W key, the currY coordinate switches to that denoting IMAGE_START_NORTH_Y. If you cross check with the constants.js file, you will realise that the value is 0.

If you look at the spritesheet and the values denoted by all the IMAGE_START_<direction>_Y, you should understand how they are derived. Take a look at the image below.

currY explanation

You can see that the values correspond to where the game character is facing in that same direction within the spritesheet. Hence, whenever the player presses the movement key, we set currY to that corresponding y position in the sprite sheet.

Since the animation comes as a sequence of the 3 sets of images per directional facing, line 128 increases the currX by an image's width while the isMoving variable is true. After it reaches the third animation pose, it goes back to 0 as instructed by line 130.

Until Next Time

You can see how these simple codes help you move the game character around your screen. Boundary checking and some simple tweaks to make the movement more natural are missing, but you should be able to get the most basic idea on how to make use of a sprite sheet, and get your character responding to your keyboard inputs and move.

Downloads

You can view the demo of this tutorial here (opens in new window).

You can download the files used in this tutorial here.