Creating a Game Framework with JavaScript and the Canvas Element

Creating a Game Framework with JavaScript and the Canvas Element
Page content

By now we have seen what the canvas element is, how to use it and seen some effects that can only be created using the canvas. Now it’s time to start creating a framework that will allow us to use the canvas element to create a simple game.

Before we jump into the code, first take a look at the demo that will be created through the next few articles. On the surface it looks exactly like the demo from the second article, but rest assured that behind the scenes there is quite a bit of work that has been done to create a base framework that will more easily allow us to create the final game.

The code that will be presented uses JavaScript as an object oriented language. Unless you have done a lot of JavaScript programming this may seem odd at first. I recommend that those not familiar with the object oriented capabilities of JavaScript to read this tutorial, hosted by the Mozilla developer centre. It will get you up to speed on some of the programming techniques that will be used here.

Conceptually the framework will be split into two parts: the classes that deal with the underlying 2D engine (i.e. the code that handles the canvas, render loop, input etc), which will be referred to as Engine classes, and the classes that will create the objects that actually make up the game, referred to as Application classes. Since the Application classes are built on top of the Engine classes, we will focus on creating the Engine classes first.

Main.js

If you have studied the code from the previous examples you will see that a lot of that code exists in the Main file. First some global variables are defined. [code]

And, like before, we set the init function to be run once the page has been loaded. [code]

In the init function we create a new instance of the GameObjectManager class. [code]

You will notice that we call the startupGameObjectManager function on the new GameObjectManager instance. The startupClassName function will be a reoccurring theme in this and upcoming articles. These functions effectively serve as the constructor of the class, and this is done for two reasons.

First is that JavaScript doesn’t support function overloading (at least not easily). This is a problem when you want to have more than one constructor. By offloading the construction to another set of functions (e.g. startupClassName1, startupClassName2) it is quite easy to define several different ways to construct a class.

The second reason (and this is more of a personal preference) is that I constantly found myself referencing variables from the constructor that were not already defined. This is more a hang over from my own personal experience with languages like C++, Java and C# where the location in the source code where a class variable was defined had no impact on it’s visibility in the constructor function. Take the following example in C#.

class Test

{

public void Test() {this.a = 5;}

public int a;

}

This code is legal and works fine. Now take the same example in JavaScript.

function Test()

{

this.a = 5;

var a;

}

The issue here is that the local variable a does not exist at the point where we try to assign the value 5 to it. It will only exist once the code var a; has been run. While this is a fairly contrived example, it does highlight the problem I ran into. However, by keeping the construction of the class in a function like startupClassName, and defining (but not initialising) the local variables in the constructor, I could be certain that the variables I was referencing in these construction functions did actually exist.

GameObjectManager.js

First up we have the GameObjectManager class. The GameObjectManager is an Engine class that manages the canvas drawing operations, as well as distributing events to the GameObject classes (the GameObject class will be described later).

startupGameObjectManager

So, given that the initialisation of the classes will be done in the startupClassName function, the GameObjectManager will be initialised in the startupGameObjectManager function.

The global reference to the GameObjectManager instance, g_GameObjectManager, is updated to point to the new instance. [code]

References to the canvas and its drawing context are stored. [code]

In the previous examples all drawing was done directly to the canvas element. This style of rendering is generally referred to as single buffering. Here we will be using a technique called double buffering, where all drawing by the individual game objects is done to a second in-memory canvas element (the back buffer), which is then copied to the canvas element on the web page (the front buffer) in one operation.

Double buffering is normally used to reduce flickering. In my own tests I could not see any flickering while rendering directly to the canvas element, but there was anecdotal evidence on the web that some browsers did suffer from flickering with single buffered rendering.

However, double buffering also prevents the end user from seeing the final frame being built up from the drawing operation of each individual game object, which is entirely possible if some complex drawing operations (like transparency, anti-aliasing or procedural textures) are performed in JavaScript.

The memory overhead of using a second buffer is quite small, and the performance hit of doing one additional image copy operation (drawing the back buffer to the front buffer) is negligible, so there is no real downside to implementing a double buffered rendering system.

If the canvas element defined in the HTML page is considered the front buffer, then an additional canvas element needs to be created to be used as the back buffer. Here we use the documents createElement function to create an in-memory canvas that will be used as the back buffer. [code]

Next we create a new instance of the ApplicationManager class, and call startupApplicationManager to initialise it. The ApplicationManager will be described later. [code]

And finally we use the setInterval function to repeatedly call the draw function, which will serve as our render loop. [code]

draw function

The draw function is our render loop. In previous examples the render loop function directly modified the object we were drawing to the screen (the smiley face), which was fine when you were only drawing one object. But a game will be made up of dozens of separate objects, so rather than manage these objects directly inside the render loop, the draw function maintains an Array of these game objects and lets them update and draw themselves.

First the time since the last frame was rendered is calculated. Even though we has asked that the draw function be called 30 times a second, there is no guarantee that this will actually happen. By calculating the time that has passed since the last frame we can ensure that the execution of the game is as independent of the frame rate as possible. [code]

The drawing contexts are then cleared. [code]

Next the game objects (as defined by the GameObject class, which will be described later) have their update and draw functions called. Note that these functions are optional (which is why we check to see of they exist before calling them), but almost every game object will want to update and draw itself. [code]

Finally the back buffer is copied to the front buffer, so the end user can see the next frame. [code]

addGameObject and removeGameObject functions

The addGameObject and removeGameObject function allow the GameObject classes to add and remove themselves from the collection of GameObjects maintained by the GameObjectManager in the gameObjects variable.

The GameObjectManager class is the most complicated of the classes that we will be creating as part of the game framework. In the next article we will take a look at the GameObject, VisualGameObject, Bounce and ApplicationManager classes, which make up the rest of the framework.

Check out the demo here, download the source code here, and browse the code documentation here.

Read more in the Game Development with JavaScript and the Canvas element series.