Javascript Game Engine Part 1 – Sprite drawing & animation

I always wanted to program a game engine and this easter weekend boredom gave me some time to do so. I thought this exercise could help other people so I will describe here the different steps to build such an engine. Here is the first part of this tutorial series.

Canvas definition and basic drawing

First we will need an HTML element to draw onto it. The <canvas> element is what we are looking for. You need to specify its size explicitly as it will be the available pixels we have in our canvas which can be different than the actual size on the screen defined with CSS properties. This is an important point, canvas has an intrinsic size (which define the available size for all drawing functions) and an extrinsic size (real size on the page) for which the canvas scale automatically. If you want to keep it simple, only define the intrinsic size and do not define the size of the canvas in CSS.

<canvas id="game-window" width="1024" height="640"></canvas>

From the javascript endpoint we require a specific object to actually draw on the canvas. This object (CanvasRenderingContext2D) is available from the canvas element:

var gameCanvas = document.getElementById('game-window');
var context = gameCanvas.getContext('2d');

And now come the interesting part, we will draw something onto the canvas. I am not describing every possible drawing function here, you’ll find all you need on the CanvasRenderingContext2D reference page.

// Draw a red rectangle from (100, 100) to (200, 200)
context.strokeStyle ="#f00";
context.strokeRect(100, 100, 200, 200);

// Get an image from an url
var img = new Image();
img.src = "imageurl.jpg";
// Wait for image to be loaded before rendering it
img.onload = function() {
  // Draw image at position (100,100)
  context.drawImage(img, 100, 100);
};

Game loop in javascript

If you’ve wrote a game before you know that at the core of every game you’ll allmost allways find the same paradigm. A game loop responsible for updating the environment (update scene, move sprites, handle user input) and rendering the game. This loop is infinite and only exit when the game ends. The same paradigm applies here however as the javascript is mono-threaded we need a way to ensure the browser can have some “spare” time to do its internal work. This is done by delegating the loop to the browser, we just ask to be called for each frame update. Modern browsers offer the requestAnimationFrame method to do so.

function update() {
  // Update environment
  // ...
}

function render() {
  var gameCanvas = document.getElementById('game-window');
  var context = gameCanvas.getContext('2d');
  // Render to canvas
  // ...
}

function gameloop() {
  update(); // Update environment
  render(); // Render to canvas

  // request the browser to call again gameloop function
  requestAnimationFrame(gameloop);
}

// First call to start game loop
requestAnimationFrame(gameloop);

In this example I haven’t included the timing function required to update the environment according to the elapsed time. Again this is a common pattern in game programming. If you want to know more about it you can read this article about the game loop. Here is the example with this pattern implemented:

/**
* Timestamp function
* @return {integer} a timestamp
*/

function getTimestamp() {
  return window.performance &amp;&amp; window.performance.now ? window.performance.now() : new Date().getTime();
}

/**
* Update game environment
* @param {int} dt time elapsed
*/

function update(dt) {
  //...
}

/**
* Render game scene
* @param {2DContext} context
* @param {int} dt elapsed time
*/

function render(context, dt) {
  //...
}

var step = 1/30; // This the delay between each environment update
var dt = 0;
var last = 0;
var gameCanvas = document.getElementById('game-window');
var context = gameCanvas.getContext('2d');

function gameloop() {
  var now = getTimestamp();

  // Add elapsed time since last call
  dt = dt + Math.min(1, (now - last) / 1000);

  // Here we call the update method at given interval
  // catching up time if necessary
  while(dt &gt; step) {
    dt = dt - step;
    update(step);
  }

  render(context, dt); // Render to canvas

  // request the browser to call again gameloop function
  requestAnimationFrame(gameloop);
}

last = getTimestamp();
// First call to start game loop
requestAnimationFrame(gameloop);

Sprite animation

So we have seen how to render an image and how to construct a game loop in javascript. We have everything needed to animate a sprite. A sprite animation is pretty straightforward as it is only a sequence of different frames of the same sprite rendered in a loop.
For example if we have the following sprite composed of different frame, all in the same image : ship-1-r
We can render each frame alternatively which will “animate” the sprite. The update() function will be responsible for determining which frame to render and render() will render the frame.

Here is an object oriented implementation of an animated sprite:

"use strict";
/**
 * Animated sprite
 * @param {string} url      Url of the image containing sprite frames
 * @param {int} frameWidth  width of individual frame
 * @param {int} frameHeight height of individual frame
 * @param {int} frameSpaceX horizontal margin between frames
 * @param {int} frameSpaceY vertical margin between frames
 * @param {int} frameCountX frame count per row
 * @param {int} frameCountY frame count per col
 */

function AnimatedSprite(url, frameWidth, frameHeight, frameSpaceX, frameSpaceY, frameCountX, frameCountY) {
  this.loaded = false;
  this.frameWidth = frameWidth;
  this.frameHeight = frameHeight;
  this.frameCountX = frameCountX;
  this.frameCountY = frameCountY;
  this.frameSpaceX = frameSpaceX;
  this.frameSpaceY = frameSpaceY;

  this.image = new Image();
  this.image.src = url;

  this.frameCount = this.frameCountX * this.frameCountY;
  this.frameIndex = 0;
  this.frameX = 0;
  this.frameY = 0;

  this.x = 0;
  this.y = 0;

  var self = this;
  this.image.onload = function() { self.loaded = true; };

}

/**
 * Reset animation
 */

AnimatedSprite.prototype.reset = function() {
  this.frameIndex = 0;
  this.frameX = 0;
  this.frameY = 0;
};

/**
 * Update animation by cycling between frames
 * @param  {int} dt elapsed time
 */

AnimatedSprite.prototype.update = function(dt) {
  this.frameIndex = (this.frameIndex + 1) % this.frameCount;  
  this.frameX = (this.frameWidth + this.frameSpaceX) * (this.frameIndex % this.frameCountX);
  this.frameY = (this.frameHeight + this.frameSpaceY) * ~~(this.frameIndex / this.frameCountX);
};

/**
 * Render sprite
 * @param {2DContext} context
 * @param {int} dt elapsed time
 */

AnimatedSprite.prototype.render = function(context, dt) {
  context.drawImage(this.image,
    this.frameX, this.frameY, this.frameWidth, this.frameHeight,
    this.x, this.y, this.frameWidth, this.frameHeight);
};


/**
 * Set position of the sprite on the canvas
 * @param {int} x
 * @param {int} y
 */

AnimatedSprite.prototype.setPosition = function(x, y) {
  this.x = x;
  this.y = y;
};


/**
 * Move the sprite by the specified direction
 * @param  {int} x
 * @param  {int} y
 */

AnimatedSprite.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
};

In our main game update and render we then only have to call this object update and render methods :

var myAnimatedSprite = new AnimatedSprite('ship-1-r.png', 100, 94, 1, 1, 1, 4);
myAnimatedSprite.setPosition(100, 100);

/**
* Update game environment
* @param {int} dt time elapsed
*/

function update(dt) {
  //...
  myAnimatedSprite.update(dt);
}

/**
* Render game scene
* @param {2DContext} context
* @param {int} dt elapsed time
*/

function render(context, dt) {
  //...
  myAnimatedSprite.render(context, dt);
}

Source Code

With the help of the tutorial you should be able to animate sprites on your screen. The enhanced source code for this tutorial is available here: JS Game Engine part 1 source code. I have also added the source of a basic scrolling background object.

Have fun !