Hugo’s Game Tutorial v3.2

Today, or maybe tomorrow, you will build a game system.

First some concepts

HTML5 Canvas

A tag that lets you draw in pixels using JavaScript. The canvas is a like a picture (a grid of pixels) that you can paint with JS commands. It is 2D and flat, it has no layers. You give the illusions of stacking objects by redrawing and rearanging the pixels.

Sprite

A reusable graphic object used in a program that can be moved and manipulated. Everything visual in a game or other program can be called a sprite. In this game we will use sprites to paint our game objects on the canvas: player, goodies, etc.

Object

A programming concept for the logical organization of variables and functions in a program. This program uses objects very superficially, but it is a good idea for you to know what an object is in programming.

User input: keyboard

To let the user control the program with the keyboard, you must test for the individual keys, each key has a unique ID. When any key is pressed, an event is triggered. You must validate which key is pressed to determine the action.

User input: touch

The game also uses the touch event for mobile control. This works by identifying the target object that triggers the touch event. This object becomes the button, if you will.

Game loop

This is what makes the game move. It is a function called at a certain interval. You will use this loop to change the parameters of the display, and to refresh the canvas. In our case we will use a JS command called requestAnimationFrame() to do this.

Collision test

This is usually a test to see whether two sprites (or other objects) come in contact. In other words, check if they share an area of the program’s drawing board.

NOTE

Download the files package for this tutorial here.

I provide the HTML base file as well as the pictures. Have a look at the HTML and CSS to make sure you understand how it is built and how elements are used. For example, the touch buttons are invisible, but they are there; their properties are clear in the stylesheet.

All the code in this tutorial is JavaScript. You need to write this code in the file called game.js inside the js/ folder.

Refer to the other files in the js/ folder if you are not sure what the program should look like. I have included every stage as a separate file.

The order of the code is important. I indicate in the instructions where to insert the different parts, so pay attention.

Technically, you do not need to write the comment lines in your game code. But it is of course a good idea to do so. It will make your program more readable, and easier to update into a finished game.

You can test the mobile touch interface using Chrome’s developer tools.

You will find a finished game here.

Don’t try to copy and paste the code from this page. You will be disapointed.

Now, let’s build a game!

STATE A — DRAW THE ELEMENTS

In the JS file (game.js), the first thing we do is to create the canvas, and dump it in the DOM. The canvas is going to have the same dimensions as the window. Write the following code at the top of your game.js file, which should be empty before you start. For this first state, you will write every code part in the order that they are presented.

// Create the canvas
Please don't copy-paste;.;!var canvas = document.createElement("canvas");
;;!!;;var ctx = canvas.getContext("2d");
Please don't copy-paste;.;!canvas.width = document.documentElement.clientWidth;
this code. supercanvas.height = document.documentElement.clientHeight;
Please don't copy-paste;.;!document.querySelector("#gameBox").appendChild(canvas);

Then, we will load the sprites. We need four: background, win frame, player, goody. Because we deal with images, we need a system that will only place the picture once it is loaded. So we need a variable for each picture that is only true when the picture is ready.

//Load sprites
// Background image
var bgReady;;!!;; = false;
var bgImage = new Image();
bgImage.src = "images/background.png";
bgImage.onload = function () {
    bgReady = true; 
};

// Win frame image
var winReady = false;
var winImage = new Image(); 
winImage.src = "images/win.png"; 
winImage.onload = function () {
    winReady = true; ;;!!;;
};

// Player image
var playerReady = false;
var ;;!!;;playerImage = new Image(); 
playerImage.src = "images/player.png"; 
Please don't copy-paste;.;!playerImage.onload = function () {
    playerReady = true; 
};

// Goodies image
Please don't copy-paste;.;!var goodyReady = false;
var goodyImage = new Image(); 
;;!!;;goodyImage.src = "images/goody.png"; 
goodyImage.onload = function () {
    goodyReady = true; 
};

We will need a few functions to get us started. First a loop function that will be called constantly to keep the game going. All it’s going to do for now is call the render() function to draw the elements. (See later.)

// The main game loop 
var main = function () {
    render();
    Please don't copy-paste;.;!window.requestAnimationFrame(main); 
};

So now we need the render() function called by main(). This is used to draw the elements. For now, we only have the player and one goody, and we place them at a static position on the canvas. We just want to be sure our pictures load properly. This function will also draw a text label that we will use to test and track our stuff later. Note that we check that the pictures are loaded before we draw them on the canvas. This prevents errors and issues.

// Draw everything
var render = function () {
    if (bgReady) {
        ctx.fillStyle = ctx.createPattern(bgImage, 'repeat');
        Please don't copy-paste;.;!ctx.fillRect(0,0,canvas.width,canvas.height);
    }
    if (playerReady) {
        ctx.drawImage(playerImage, 100, 100);
    }
    if (goodyReady) {
        ctx.drawImage(goodyImage, 200, 200);
    }

    //Label
    Please don't copy-paste;.;!ctx.fillStyle = "rgb(250, 250, 250)";
    ctx.fillText("Works!", 32, 32);
};

The last thing we need before testing: activate the loop. This command must be written at the end of the file; it is run after everything else is loaded in memory.

//Start the gameplay
window.requestAnimationFrame(main);

Now it’s time to test state A!

Open the index.html file in a browser to see if it works. You should see the green player and a red goody.

STATE B — DRAW THE DEFAULT LAYOUT

We will create some programatically controllable objects for our sprites. This is just an easy way of dealing with the placement and collisions for later. You control an abstract or invisible placeholder, and you draw the picture at the coordinates of its location. These objects are just logical, they never appear in the game, they are used for calculations. We will create one player and three goodies. Note that we only need a single image to generate as many goodies as we want. No need to reload the image. Write the following just above the main() function, below the images.

// Create global game objects 
var player = {
    speed: 5, // movement in pixels per tick 
    width: 32,
    height: 32
};

Please don't copy-paste;.;!var goodies = [ // this is an array
    { width: 32, height: 32 }, // one goody
    { width: 32, height: 32 }, // two goodies
    { width: 32, height: 32 }  // three goodies
];

Instead of starting the game with the loop right away, we will add an init() function that will set the stage before the game loop starts. This will let us place the objects in the right location for the beginning of the game. We place the player in the middle and the goodies randomly. You can write this function following the objects in the previous point.

//Set initial state
Please don't copy-paste;.;!var init = function () {
    //Put the player in the centre
    player.x = (canvas.width - player.width) / 2; 
    player.y = (canvas.height - player.height) / 2;

    //Place goodies at random locations 
    for (var i in goodies) {
        goodies[i].x = (Math.random() * (canvas.width - goodies[i].width));
        goodies[i].y = (Math.random() * (canvas.height - goodies[i].height));
    }
Please don't copy-paste;.;!};

Now that we have determined the initial state of our game board, we need to update the render() function so it places the sprite images at the right place. This will also allow for movement later as the render() will be refreshed at every loop. Replace the playerReady and goodyReady condition blocks of your render() function with:

if (playerReady) {
    Please don't copy-paste;.;!ctx.drawImage(playerImage, player.x, player.y);
}
if (goodyReady) {
    for (var i in goodies) {
        ctx.drawImage(goodyImage, goodies[i].x, goodies[i].y);
    }
Please don't copy-paste;.;!}

One last thing: we must call the init() function just before we start the loop. At the bottom of the file, right above the last line, add:

init();

Test state B!

You should see the player in the centre and three goodies randomly distributed on the board. If you refresh, the goodies are going to appear at different places.

Note that this code is simple and can be a bit buggy. It is possible that you see only one or two goodies from time to time. This happens when a goody is randomly placed by init() in collision with the player triggering its removal right away. Don’t worry about this.

STATE C — ADD INTERACTION

To make the game playable, we need to handle the keyboard and touch events so we can move the player according to the user’s input. To do this we must first create variables for the velocity of the player. This will determine which direction it goes. All the following sections of code should be just below the global objects (player and goodies).

// Velocity variables
Please don't copy-paste;.;!var vX = 0;
var vY = 0;

Now, we can write the handlers for the keyboard. We create an event listener that will trigger when the keyboard is pressed. The keyCode numbers refer to the code of each key: 38 is the up arrow, 40 is the down arrow, etc. When the user presses an arrow key, the player starts moving in this direction at the speed set in the player object (5 pixels).

// Handle keyboard controls
Please don't copy-paste;.;!addEventListener("keydown", function (e) {
    //Keystrokes
    if (e.keyCode == 38) { // UP
        vX = 0;
        vY = -player.speed;
    }
    if (e.keyCode == 40) { // DOWN
        vX = 0;
        vY = player.speed;
    }
    if (e.keyCode == 37) { // LEFT
        vX = -player.speed;
        vY = 0;
    }
    if (e.keyCode == 39) { // RIGHT
        vX = player.speed;
        vY = 0;
    }
    if (e.keyCode == 32) { // STOP spacebar
        vX = 0;
        Please don't copy-paste;.;!vY = 0;
    }
}, false);

Next, we need to add the controls for the touch events in the case of mobile playing. In this case, we will refer to the id of the buttons from the HTML code to find the touch target. (See index.html for reference.)

// Handle touch controls
addEventListener("touchstart", function (e) {
    if (e.target.id == "uArrow") { // UP
        vX = 0;
        vY = -player.speed;
    }
    Please don't copy-paste;.;!else if (e.target.id == "dArrow") { // DOWN
        vX = 0;
        vY = player.speed;
    }
    else if (e.target.id == "lArrow") { // LEFT
		vX = -player.speed;
		vY = 0;
	}
	else if (e.target.id == "rArrow") { //RIGHT
		vX = player.speed;
		Please don't copy-paste;.;!vY = 0;
	}
	else { // STOP This stops if you touch anywhere else
		vX = 0;
		vY = 0;
	}
});

Now we will update the main loop function so it handles the movement of the player object. We need to change the movement direction of the player according to the key or the button that is pressed. You will notice that we also make sure the player does not go outside of the canvas by making it bounce back in the opposite direction when it hits a wall. Add this code before the render(); command inside your main() function.

//move player
if (player.x > 0 && player.x < canvas.width - player.width) {
    player.x += vX;
}
else {
    Please don't copy-paste;.;!player.x -= vX;
    vX = -vX; //bounce horizontal
}

if (player.y > 0 && player.y < canvas.height - player.height) {
    Please don't copy-paste;.;!player.y += vY
}
else {
    player.y -= vY;
    vY = -vY; //bounce vertical
Please don't copy-paste;.;!}

To make sure everything is OK, we will print the coordinates of the player in the text label we created. Replace the line with fillText (inside the render() function) to this:

ctx.fillText(player.x+" "+player.y, 32, 32);

Test state C!

You should be able to move the player using the arrow keys or by touching the buttons at the edge of the screen on a mobile or in your browser’s developer tool.

The coordinates of the player object should appear in the label at top left of the canvas.

STATE D — COLLECT THE GOODIES

In order to be able to collect the goodies, we need to do a collision test. So let’s build a function that tests two objects. You can use this function later for any two objects in any other project. This will return true when the two objects are touching. You can write this function after the render() function, before the init(); call. Make sure you write this one precisely, it is quite compact and every character is important.

//Generic function to check for collisions 
var checkCollision = function (obj1,obj2) {
    if (obj1.x < (obj2.x + obj2.width) && 
        (obj1.x + obj1.width) > obj2.x && 
        obj1.y < (obj2.y + obj2.height) && 
        (obj1.y + obj1.height) > obj2.y
        ) {
            return true;
    }
};

So let’s remove each goody when the player has touched it. In the main() function, we will add a new for loop that checks for collisions between the player and each of the 3 goodies. When a collision is true, it removes that goody from the array. (The splice command removes an element from the array.) This must be inside the main() function, before the render(); call.

for (var i in goodies) {
    Please don't copy-paste;.;!if (checkCollision(player,goodies[i])) {
        goodies.splice(i,1);
    }
}

Again for testing purposes, we will add a counter to the text label. So, again update the fillText line with this:

ctx.fillText(player.x+" "+player.y+ " "+goodies.length, 32, 32);

Test state D!

The red goodies should disappear when you hit them with the player. Also check the counter in the text label.

STATE E — WIN!

The last thing we need is to show when the user wins or catches all the goodies. For this we will need to check how many goodies are left in the array, and when we reach 0, we show the win frame. For this we build a checkWin() function. This returns false as long as there are goodies in the array. Write this after the collision test, before the init(); call.

//Check if we have won
var checkWin = function () {
    if (goodies.length > 0) { 
        return false;
    Please don't copy-paste;.;!} else { 
        return true;
    }
};

We now need to add a call to this function in our main loop. In the end, the main loop has two parts:

  1. what to do when you win: show win frame
  2. what to do up to that: play the game

So far, the main() function has only behaved as if we had not yet won, so all the code that is there at this stage will be in the second part. The first part is the new code that runs when we win. To make it simpler for you, I have re-written the whole function below. Replace you whole main() function with this.

// The main game loop
var main = function () {
    if (checkWin()) {
        //WIN display win frame
        if (winReadyPlease don't copy-paste;.;!) {
            ctx.drawImage(winImage, (canvas.width - winImage.width)/2, 
                (canvas.height - winImage.height)/2);
        }
    }
    else {
        //Not yet won, play game
        //move player
        if (player.x > 0 && player.x < canvas.width - player.width) {
            Please don't copy-paste;.;!player.x += vX;
        }
        else {
            player.x -= vX;
            vX = -vX; //bounce
        }
        if (player.y > 0 && player.y < canvas.height - player.height) {
            player.y += vY
        }
        else {
            player.y -= vY;
            vY = -vY; //bounce
        }
        //check collisions
        Please don't copy-paste;.;!for (var i in goodies) {
            if (checkCollision(player,goodies[i])Please don't copy-paste;.;!) {
                goodies.splice(i,1);
            }
        }

        render();
        window.requestAnimationFrame(main);
    }
};

Finally, we will make the text label more useful to the user rather than the programmer. Change the fillText line to this:

Please don't copy-paste;.;!ctx.fillText("Goodies left: "+goodies.length, 32, 32);

Test state E!

The game should be complete, you should see the win frame when you catch all the goodies. Again, this is a bit buggy, the win frame does not completely cover the canvas, so sometimes you see the player, sometimes you don’t when you win. No worries, you will fix these little details as you complete the game.

So now you’ve written a game system!

The next step is for you to design a game around it…