Friday, October 14, 2011

Loading HTML5 Game Assets

Before diving into HTML5 game development, I built a few conventional web sites (like this one - nice snapshots of the Blue Ridge). With conventional web design, page assets like images are loaded once the page loads, slowly filling out the blank screen with colorful pictures. This works well for web pages, since you can immediately begin reading the content and hardly notice the small delay as the images are painted in.

However, the wait for in-game assets can be especially long when downloading 10MB worth of images and sounds for a game like Entanglement. With Entanglement, the first issue I came across was that I couldn't draw a beautiful wooden tile to the gameboard canvas if the wooden tile image hadn't been downloaded yet.

My first attempt to solve this problem was to throw a JavaScript try/catch block around the drawImage calls so they wouldn't break the game if the image wasn't available yet. This resulted in the game starting immediately, but the gameboard slowly appeared as the images were downloaded. This is fine behavior for a web page but seemed a bit ugly for a web game.

My second attempt was a bit better: I included all the images in the html document as invisible <img> tags. This ensures that the images are indeed loaded before the game began but had the unfortunate side effect that JavaScript processing didn't start until the entire page was downloaded (including all the referenced images). This caused the long wait at a "Loading..." screen that couldn't be dynamically updated with a progress bar since JavaScript wasn't running yet.
It's doing something but we're not sure what nor how long it's going to take.
Enter the Asset Loader! This tool allows larger assets to be loaded after the page loads and the JavaScript processing begins. We're in the prototype stage of our next game, and I took this opportunity to build a proper asset loader. Seth Ladd wrote a wonderful tutorial on the basic steps to set up a simple asset manager. Using his insight and including a bit more code to handle audio assets, we created an asset loader that loads assets as well as calls two functions we can supply to track progress: what to do when a single asset is finished loading and what to do when all the assets are finished loading.
function loadAssets(assetList, onAssetLoaded, onAllAssetsLoaded){...}
The next piece we want is a nice progress bar. The <progress> HTML5 element has two properties we care about for keeping tabs on our loaded assets: max and value. Using these two properties we can create a very simple progress bar class to hand off to the asset loader that might look something like this:
function progressBar(parent){
 this.element = document.createElement('progress');
 parent.appendChild(this.element);
 
 // initialize the progress bar at zero percent
 this.element.setAttribute('value', 0);
 this.element.setAttribute('max', 1000);
 
 // for this example, progress will be some value [0,1]
 this.update = function (progress) {
  this.element.setAttribute('value', progress * 1000);
 };
};
We can then set up our asset loader to update the progress bar each time an asset is finished loading, by feeding the progressBar's update function the current state of loaded assets. In our asset loader, the "ratio" value shown below is simply the number of assets loaded divided by the total number of assets.
var assetProgress = new progressBar(document.getElementById('loading-screen'));
loadAssets(assetList, function(ratio){assetProgress.update(ratio);}, startGame);

We've already implemented the progress bar in the latest builds of both Thwack!! and Entanglement if you care to see it in action!