Build a vertical scrolling shooter game with HTML5 canvas – Part 1

February 6th, 2011

I’ve always found that, for me at least, one of the best ways to learn a new programming language is to make a game. It’s how I learned ActionScript 2 and then ActionScript 3 and it’s really helped me learn JavaScript. I’ve become a bit obsessed with the canvas tag over the last couple of weeks and I really wanted to see how easy or difficult to create a game with it compared to Flash. To see how difficult it is, we’ll build a vertical scrolling shooter game.

We’re going to start off by creating a ship that we can move around and then five enemy ships that move from the top to the bottom of the canvas. We won’t worry about game graphics in this part, we just want to get the functionality down and then build on that. I’m not going to worry about compatability with browsers like IE8, if the canvas doesn’t work in a browser, that’s it’s problem. This is just an exercise in learning how to make something that will work with a modern browser.

Let’s get started. We only need one line of HTML:

1
<canvas id="canvas" width="600" height="600"></canvas>

And a little bit of CSS so we can see what’s going on:

1
2
3
4
5
6
7
8
9
10
11
body {
  padding:0;
  margin:0;
  background:#666;
}
canvas {
  display:block;
  margin:30px auto 0;
  border:1px dashed #ccc;
  background:#000;
}

Now, let’s get onto the part that will make this game run, the JavaScript. First, we’ll get our ship working, so we need to set up some variables.

1
2
3
4
5
var canvas,
    ctx,
    width = 600,
    height = 600,
    ship_x = (width / 2) - 25, ship_y = height - 75, ship_w = 50, ship_h = 50;

I’m not going to go over the basics of the canvas tag. If you don’t know how it works, you can learn more here. The canvas and ctx variables are for find in the canvas and then drawing on it, width and height are the canvas dimensions. The ship variables are for both the initial positioning and dimensions of the square that’s going to represent the ship for now. Next let’s write the code that will actually draw the ship block on the canvas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function clearCanvas() {
  ctx.clearRect(0,0,width,height);
}

function drawShip() {
  ctx.fillStyle = '#0f0';
  ctx.fillRect(ship_x, ship_y, ship_w, ship_h);
}

function init() {
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext('2d');
  setInterval(gameLoop, 25);
}

function gameLoop() {
  clearCanvas();
  drawShip();
}

window.onload = init;

So this code will get us this. A green block near the bottom of the canvas. Let’s go through the functions and see what’s happening here. The clearCanvas function is the something that we have to have in order for this to work. We have to redraw the canvas every time we we run the game loop and we need to clear everything on the canvas first or else we’ll get multiple copies of the ship on the canvas. So we clear it using the clearRect function and we set it to clear the entire canvas. Next we have the drawShip function where we set the fillStyle to green and then use fillRect to set the ship’s x and y positions and it’s width and height. The init function is what we use with the window.onload function at the bottom to get everything started. We get the canvas element, then get it’s context and finally, we use setInterval to call the gameLoop function every 25 milliseconds. With the gameLoop function we call clearCanvas and drawShip. This is the heart of our game, the init function sets everything up and then gameLoop makes it run.

Now, let’s make our ship move. Add these to the variables at the top:

1
2
3
4
rightKey = false,
leftKey = false,
upKey = false,
downKey = false

Then change the init function to this:

1
2
3
4
5
6
7
function init() {
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext('2d');
  setInterval(gameLoop, 25);
  document.addEventListener('keydown', keyDown, false);
  document.addEventListener('keyup', keyUp, false);
}

We’ve added event listeners to listen for both keys being pressed down and keys being released. Now add these two functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
function keyDown(e) {
  if (e.keyCode == 39) rightKey = true;
  else if (e.keyCode == 37) leftKey = true;
  if (e.keyCode == 38) upKey = true;
  else if (e.keyCode == 40) downKey = true;
}

function keyUp(e) {
  if (e.keyCode == 39) rightKey = false;
  else if (e.keyCode == 37) leftKey = false;
  if (e.keyCode == 38) upKey = false;
  else if (e.keyCode == 40) downKey = false;
}

Here, we are checking to see which key is being pressed and if it’s one of the arrow keys, then we change the corresponding variable to true and when that arrow key is released, we change the variable back to false. This will all set up the movement of the ship. Now, change the drawShip function to this:

1
2
3
4
5
6
7
8
9
10
11
12
function drawShip() {
  if (rightKey) ship_x += 5;
  else if (leftKey) ship_x -= 5;
  if (upKey) ship_y -= 5;
  else if (downKey) ship_y += 5;
  if (ship_x <= 0) ship_x = 0;
  if ((ship_x + ship_w) >= width) ship_x = width - ship_w;
  if (ship_y <= 0) ship_y = 0;
  if ((ship_y + ship_h) >= height) ship_y = height - ship_h;
  ctx.fillStyle = '#0f0';
  ctx.fillRect(ship_x, ship_y, ship_w, ship_h);
}

Now we are checking to see if the left or right arrow key is pressed and if so, move the ship 5 pixels in that direction, the same with the up and down arrows. At this point, we’ll have this. Ok, now let’s add some enemies to our game. First add these variables to the ones at the top of the page:

1
2
3
4
5
6
7
enemyTotal = 5,
enemies = [],
enemy_x = 50,
enemy_y = -45,
enemy_w = 50,
enemy_h = 50,
speed = 3

We are setting the total number of enemies, the array we’ll store our enemies in, the positioning of the first enemy, the width and height and finally, the speed our enemies will move at. Now, we’ll use a for loop to add our enemies to our enemies array:

1
2
3
4
for (var i = 0; i < enemyTotal; i++) {
  enemies.push([enemy_x, enemy_y, enemy_w, enemy_h, speed]);
  enemy_x += enemy_w + 60;
}

With the loop we’re also increasing the x position of each enemy by 60 in order to spread them out across the canvas. Next, we have to actually draw our enemies onto the canvas:

1
2
3
4
5
6
function drawEnemies() {
  for (var i = 0; i < enemies.length; i++) {
    ctx.fillStyle = '#f00';
    ctx.fillRect(enemies[i][0], enemies[i][1], enemy_w, enemy_h);
  }
}

We just run another for loop and draw an enemy for each enemy that’s in the enemies array. And as it runs through the entire array, it uses the individual x and y positions of each enemy in the array. This is important now for the x position of each enemy and will be import down the road when we want to replace dead enemies. Next, let’s get the enemies to move.

1
2
3
4
5
6
7
8
9
function moveEnemies() {
  for (var i = 0; i < enemies.length; i++) {
    if (enemies[i][1] < height) {
      enemies[i][1] += enemies[i][4];
    } else if (enemies[i][1] > height - 1) {
      enemies[i][1] = -45;
    }
  }
}

With this function, we’re running a for loop to move each enemy and checking to see if they’ve passed beyond the bottom of the canvas. If they have, then we move them back to above the top of the canvas, so it seems like an new enemy has entered. We still have one more thing to do, we need to change the gameLoop function to actually call these functions.

1
2
3
4
5
6
function gameLoop() {
  clearCanvas();
  moveEnemies();
  drawEnemies();
  drawShip();
}

Now, you should have this. We’ve got the basics of our game and next time, we’ll change the colored boxes to actual ship graphics and give our ship the ability to shoot lasers. Here’s the complete code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML5 Canvas Shooter Game Tutorial Part 1</title>
<style>
body {
  padding:0;
  margin:0;
  background:#666;
}
canvas {
  display:block;
  margin:30px auto 0;
  border:1px dashed #ccc;
  background:#000;
}
</style>
<script>
var canvas,
    ctx,
    width = 600,
    height = 600,
    enemyTotal = 5,
    enemies = [],
    enemy_x = 50,
    enemy_y = -45,
    enemy_w = 50,
    enemy_h = 50,
    speed = 3,
    rightKey = false,
    leftKey = false,
    upKey = false,
    downKey = false,
    ship_x = (width / 2) - 25, ship_y = height - 75, ship_w = 50, ship_h = 50;

for (var i = 0; i < enemyTotal; i++) {
 enemies.push([enemy_x, enemy_y, enemy_w, enemy_h, speed]);
 enemy_x += enemy_w + 60;
}

function clearCanvas() {
 ctx.clearRect(0,0,width,height);
}

function drawEnemies() {
 for (var i = 0; i < enemies.length; i++) {
                ctx.fillStyle = '#f00';
   ctx.fillRect(enemies[i][0], enemies[i][1], enemy_w, enemy_h);
 }
}

function drawShip() {
 if (rightKey) ship_x += 5;
 else if (leftKey) ship_x -= 5;
 if (upKey) ship_y -= 5;
 else if (downKey) ship_y += 5;
 if (ship_x <= 0) ship_x = 0;
 if ((ship_x + ship_w) >= width) ship_x = width - ship_w;
  if (ship_y <= 0) ship_y = 0;
 if ((ship_y + ship_h) >= height) ship_y = height - ship_h;
  ctx.fillStyle = '#0f0';
  ctx.fillRect(ship_x, ship_y, ship_w, ship_h);
}

function moveEnemies() {
  for (var i = 0; i < enemies.length; i++) {
   if (enemies[i][1] < height) {
     enemies[i][1] += enemies[i][4];
   } else if (enemies[i][1] > height - 1) {
      enemies[i][1] = -45;
    }
  }
}

function init() {
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext('2d');
  setInterval(gameLoop, 25);
  document.addEventListener('keydown', keyDown, false);
  document.addEventListener('keyup', keyUp, false);
}
function gameLoop() {
  clearCanvas();
  moveEnemies();
  drawEnemies();
  drawShip();
}

function keyDown(e) {
  if (e.keyCode == 39) rightKey = true;
  else if (e.keyCode == 37) leftKey = true;
  if (e.keyCode == 38) upKey = true;
  else if (e.keyCode == 40) downKey = true;
}

function keyUp(e) {
  if (e.keyCode == 39) rightKey = false;
  else if (e.keyCode == 37) leftKey = false;
  if (e.keyCode == 38) upKey = false;
  else if (e.keyCode == 40) downKey = false;
}

window.onload = init;
</script>
</head>

<body>
  <canvas id="canvas" width="600" height="600"></canvas>
</body>
</html>

3 Responses to Build a vertical scrolling shooter game with HTML5 canvas – Part 1

  1. matt says:

    Hey Mike,

    This is a very informative tutorial, thank you for it! I have a quick question that might be kind of obvious. I’m not quite sure how the event listeners work and would like to know if you could clarify a bit. From what I understand you call addEventLIstener() to the page to determine what is being done by the user regarding any type of event, but I’m not quite sure I understand how the code is getting the actual key codes for the events.

    In the code you pass an ‘e’ parameter to the keyDown and keyUp function and I don’t quite understand how the code knows what to do with that parameter or how it actually goes about getting the keyCode for that event and was wondering if you could clarify a bit. I have tried to research it on the web but haven’t really found a good answer as to why that e parameter has a keyCode attribute.

  2. Valstorm says:

    Just thought I’d clarify for anybody else wondering about Matt’s question:

    “In the code you pass an ‘e’ parameter to the keyDown and keyUp function and I don’t quite understand how the code knows what to do with that parameter or how it actually goes about getting the keyCode for that event”

    When an event is fired in the browser window (for example a mouse click or a key is pressed) and you have set up an event listener to ‘wait’ for this event to happen, the browser will fire the function that was told to wait for that event when the listener was set up.

    Usually the event type (in this example a key is pressed) will carry with it a parameter that identifies the source of the event. For event types involving key presses; the event will carry with it the ‘code’ for the key that was pressed.

    It’s common practice in web programming to label this event parameter as ‘e’ or ‘event’, and by using the identifier ‘e.keyCode’ you can retrieve the value stored in the keyCode parameter – then it’s a simple matter of comparing this value against a list of known codes for certain keys. If you know the key being pressed is an arrow key, you can run the code that updates the player’s ship position in that key direction.

    For a list of Key Codes:
    http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes

  3. John Blanda says:

    Excellent tutorial. Good communication of these fundamentals are very hard to find. Thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *