Writing your my our first game using html5 and canvas
published 9 jun 2012
I recently decided to write an HTML5 game using the canvas
tag. I’d made some little animated toys using canvas
with my particle systems,
but now I wanted to do something bigger and more interactive. Even on the small projects
before, the code had a habit of turning into a horrible fucking mess of procedural crap
that was too hard to follow. I couldn’t imagine building something more ambitious in that
same ad-hoc style.
Like everything that can be accomplished with programming, I’ve always assumed I know how to write a game. After all, I know to program. The rest is just filling in the details. With this game, I decided to put that theory to the test.
Choosing my first game
For my first game, I wanted to write something simple, but still with the key components of a game, like animation, collision detection, and player input. I picked a game I remembered played on my TI-83 calculator in high school:
In the game, you control the blue circle. As the platforms move up, you have to steer yourself into the gaps to avoid getting crushed to a poorly-rendered red pulp by the spikes.
Structuring a game
The structure of a game in pseudo-code is as follows. In reality, we want to split the game up into some nice objects, so that it’s clearer who plays what role, but this is the basic skeleton.
function game() {
initialize();
// do the following in a loop that executes at 60 frames per
// second
window.setInterval(function() {
//receive input
handleInput();
//update the state and position of objects
update();
// handle collisions
collide();
// draw the scene
draw();
}, 1000/60);
}
Getting started
Now, we want to fill in some of the objects we’ll use to structure our game. If you’d like more detail, check out the annotated source.
Much of the skeleton we saw before is reflected in the Game
object. The Game object
is the boss that handles coordinating the objects and running the animation loop.
// _.inherits sets up the prototype chain, so that the first argument
// is the prototype for the object in the second argument.
var Game = _.inherits(function() { }, {
'constructor': function() {
// initialize canvases
// create drawable objects
// prepare for drawing
},
'draw': function() {
// calculate time since last draw
// loop over and draw each of our drawable objects
}
});
Like any good leader, the Game object delegates the detail work to various specialists
that know exactly how to do their jobs. The Drawable
object specializes in—surprise!—drawing.
The Game
can draw all the objects in our world without caring about the specific way that happens.
Drawable also forms the prototype for more-specialized drawing objects.
var Drawable = _.inherits(function() { }, {
'state': { x: 0, y: 0, vX: 0, vY: 0 },
'constructor': function(initialState) {
// initialize state object with initial given state
},
'draw': function(dT, game) {
// multiply the velocity by the time to get a new position
// draw -- this function will be replaced in descendant classes
this.doDraw(centroid, dT, game);
// save state
},
'doDraw': function(centroid, dT, game) {
// draw the actual object
}
});
An example of a specialized Drawable
is the Background
object, which handles drawing
the scrolling background for the game.
// Background's prototype is the Drawable class
var Background = _.inherits(Drawable, {
'image': null,
'ready': false,
'constructor': function(initialState) {
// call the parent constructor
this.constructor.__super__.constructor.call(this, initialState);
// fetch the image, and track when it loads
this.image = new Image();
this.image.onload = _.bind(function() {
this.ready = true;
}, this);
this.image.src = '/posts/game/game-bg.jpg';
},
'doDraw': function(centroid, dT, game) {
if(!this.ready) return;
if(centroid.y <= 0)
centroid.y = game.height;
// draw the image in two slices
// first the top slice
game.backbufferContext.drawImage(
this.image,
/* sx */ 0,
/* sy */ this.image.naturalHeight - centroid.y,
/* sw */ this.image.naturalWidth,
/* sh */ centroid.y,
/* dx */ 0,
/* dy */ 0,
/* dw */ game.width,
/* dh */ centroid.y);
// and second the bottom slice
game.backbufferContext.drawImage(
this.image,
/* sx */ 0, /* sy */ 0,
/* sw */ this.image.naturalWidth,
/* sh */ this.image.naturalHeight - centroid.y,
/* dx */ 0,
/* dy */ centroid.y,
/* dw */ game.width,
/* dh */ this.image.naturalHeight - centroid.y
);
}
});
At this point, we have a nice structure for a game, and an animating background to boot. If you’d like, you can read the full, annotated source for the game, and here it is in action:
Next steps
At this point, if I told you this was a game, you’d call me a goddamn liar. Next we’ll add input handling, so the player can move, and then obstacles and collision detection.
Resources
Here’s the best of the million tabs I had open while I was doing this first part of the game.
- This HTML5 Canvas Cheat Sheet
for a quick reference on
canvas
. This is always open in a tab when I’m working on canvas stuff. - In case you missed it, the full annotated source for this game so far.
- MDN’s Canvas tutorials for a basic introduction to drawing with the 2d context. Their Using images page in particular has some helpful images for understanding image slicing.
- The canvas 2dcontext spec from the w3, for when you need to know exactly what your code should do.
- The annotated source for thinking about prototypal inheritance in js, especially the inherits method.
- Douglas Crockfords take on protypal inheritance in JS.