Javascript Game Foundations - Loading Assets

Tue, Dec 3, 2013

Ten Essential Foundations of Javascript Game Development

  1. A Web Server and a Module Strategy
  2. Loading Assets
  3. The Game Loop
  4. Player Input
  5. Math
  6. DOM
  7. Rendering
  8. Sound
  9. State Management
  10. Juiciness

Loading Assets

Unless your game is a text-only adventure game, you are almost certainly going to need to load some external resources such as:

  • image files - sprites, backgrounds, menu graphics, particles.
  • audio files - music, sfx.
  • json files - level data, map layouts.

Generally speaking you will probably want to load image and audio files up front while the game is starting, and then load additional level-specific data at the start of each game level.

Loading files of any kind in a browser is an asynchronous process. You will need to specify the details of what you need to load and provide a callback method to be executed once the load is complete.

Loading a Single Image

The simplest case is when all of your graphics are contained within a single image, such as a sprite sheet.

You can load a single image by constructing an <img> tag, adding an onload event listener, and setting the img src attribute:

function loadImage(src, callback) {
  var img = document.createElement('img');
  img.addEventListener('load', function() { callback(img); } , false);
  img.src = src;
}

You can start your game loop once the image has completed loading:

function run(sprites) {
  // game loop goes here
}

loadImage("images/sprites", run);

Depending on your javascript style, you might prefer to rewrite this as a nested callback:

loadImage("images/sprites", function(sprites) {
  // game loop goes here
});

Personally, I prefer to avoid nesting callbacks, but you should use whichever style you feel most comfortable with.

What do you do with your image once its loaded? well, you draw it on the canvas of course! You’ll see details of that in the (upcoming) “Rendering” article in this series.

Loading Multiple Images

In a more complex case, you might have multiple images that you wish to load:

  • player sprites
  • monster sprites
  • background images
  • map tiles (platforms, ladders, doors, trees, etc)
  • partical images

We can extend our single loadImage method into a more general purpose loadImages helper that, when called with an array of image names, will construct an <img> tag for each one, with an appropriate src, but not make the callback until all of the images have completed downloading.

We can achieve this by reference counting the completed images and making the callback once the count reaches zero.

function loadImages(names, callback) {

  var n,name,
      result = {},
      count  = names.length,
      onload = function() { if (--count == 0) callback(result); };
  
  for(n = 0 ; n < names.length ; n++) {
    name = names[n];
    result[name] = document.createElement('img');
    result[name].addEventListener('load', onload);
    result[name].src = "images/" + name + ".png";
  }

}

You can start your game loop once all of the images have completed loading:

var IMAGES = ['player', 'monsters', 'backgrounds'];

function run(images) {
  // game loop goes here
}

loadImages(IMAGES, run);

Note that the object returned to the callback method will be a hash of the loaded images, so that they can be individually referenced by the calling code:

  • images.player
  • images.monsters
  • images.backgrounds

Loading Audio Files

I’m going to cheat a little bit here and skip talking about audio assets until the last post in this series when we talk about HTML5 Audio in general. When we get to that article we will extend the loadImages method to become a more general purpose loadResources method instead.

Loading a JSON File

Finally, it can be very useful to store our level data within external .json files. These files may be hand-crafted, or they may be generated by a level editing tool such as tiled.

They are likely to contain the layout for the level map, level attributes such as name, size, color, etc, along with definitions for entities such as monsters, treasure, doors etc.

In order to load a .json file, you are going to have to make an AJAX call.

You might be tempted to reach for jQuery or some other library to perform this call, and that’s fine, especially if you’re already using such a library for other purposes. However, in modern browsers, making a simple AJAX call using the XMLHttpRequest object directly is actually fairly straight-forward:

function loadJSON(url, onsuccess) {
  var request = new XMLHttpRequest();
  request.onreadystatechange = function() {
    if ((request.readyState == 4) && (request.status == 200)) // if DONE and SUCCESS
      onsuccess(JSON.parse(request.responseText));
  }
  request.open("GET", url + ".json", true);
  request.send();
}

You might want to add a little error handling to this method, but for simple games and prototypes this will generally be adequate.

function startLevel(level) {
  // go!
}

loadJSON('level1', startLevel);