Javascript Game Foundations - Loading Assets
Tue, Dec 3, 2013Ten Essential Foundations of Javascript Game Development
- A Web Server and a Module Strategy
- Loading Assets
- The Game Loop
- Player Input
- Math
- DOM
- Rendering
- Sound
- State Management
- 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);