Due to such advantages as cross-platform and no plug-in restrictions, HTML5 games have been undergoing rapid development in recent years. We have already seen some really nice HTML5 games on the market, especially on mobile platforms, such as Angry Birds HTML5 version, Cut the Rope HTML5 version, etc. Developing an HTML5 game is not an easy task.
Ask any HTML5 game developer, compared to other languages like C++ and Java, using HTML5 for game developing is quite different, and assets loading is one of those differences. So today, we are going to dive into the assets loading problems of HTML5 game development.
Assets Loading in HTML5 Game Developments
In this article I will be trying to answer the following questions:
- How to load game assets in different types?
- How to load in batch?
- How to show the loading progress bar?
- How to store game assets?
1.1 How many assets types are in HTML5 games?
Ordinarily, game assets include image, audio, video and binary data files; for 3D games, a model file is also required, such as .obj files exported from 3DMax.
Generally speaking, these file sizes could vary from 10M to several Gs. That’s not a big deal for client side games, because game assets can be packed in an installation package; and they can be stored in local devices after installation.
But in terms of HTML5 games, it’s a little bit complicated. There are three main reasons:
- HTML5 games are dependent on web browsers.
- All the data is stored on a cloud server.
- In order to optimize web page render effects, browsers load game assets (image, audio, etc) asynchronously.
Have you ever noticed when you open some web pages, there are occasions that one or two images are blank while the whole page is displayed; after several seconds, they appear silently. That’s what I call “asynchronously”.
Except for assets like image and audio, there is a special asset type – JavaScript files. The size for JavaScript files can vary from hundreds of K to hundreds of M, especially in huge games with sophisticated game logic. So here comes the question: since browsers treat JavaScript files simultaneously, these files could create a bottle neck for game performance.
For example, in the process of interpreting the web page, the browser will interpret tags immediately as long as it appears; it will not move any further until the tag interpretation is finished, which means the whole process is stuck. More importantly, if there is an src attribute in the tag, the browser will wait until the download is finished. Therefore, sometimes we complain about the suck-ass network speed; actually, it’s not because of speed, but the long execution time of the script file, which drags loading time behind.
To solve this problem, I disguise JavaScript files into assets files, such as putting JavaScript into a JSON file as a whole string. It takes best advantage of “asynchronisity”, so that JavaScript files can be interpreted differently from other game assets. Secondly, it is advisable to shorten the interpretation time for each script file; in other words, you can modularize scripts – imitate object oriented programming methods, put different functions into different files.
But, here comes another question: JavaScript doesn’t offer the function of module inheritance. So we need to use libraries like RequireJS to imitate a similar function.
1.2 How to load game assets in different types
Use browser built-in callback API
Browsers provide a callback API to load images, which is very convenient. The basic way to programmatically load an image in a web browser is the following code:
var image = new Image(); image.addEventListener("success", function(e) { // do stuff with the image ); image.src = "/some/image.png";
As for audio, you can use Web Audio API to load game audio. But for video and binary files, it’s complicated, because there’s no effective method to make a similar function.
Use Ajax request
Ajax is capable of requesting game assets through HTTP; and the new standard of Ajax supports retrieving binary data. Then, you can take advantage of Blob or FileSystem to store data. Thus, the game assets could be converted to image, audio, etc. Here’s a little code snippet using Blob to convert assets to corresponding targets:
window.URL = window.URL || window.webkitURL; // Take care of vendor prefixes.var xhr = new XMLHttpRequest(); xhr.open('GET', '/path/to/image.png', true); xhr.responseType = 'blob'; xhr.onload = function(e) { if (this.status == 200) { var blob = this.response; var img = document.createElement('img'); img.onload = function(e) { window.URL.revokeObjectURL(img.src); // Clean up after yourself. }; img.src = window.URL.createObjectURL(blob); document.body.appendChild(img); ... } }; xhr.send(); Here's a little code snippet of using FileSystem to do the same thing: var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.addEventListener('load', function() { createDir_(root, dirname_(key).split('/'), function(dir) { dir.getFile(basename_(key), {create: true}, function(fileEntry) { fileEntry.createWriter(function(writer) { writer.onwrite = function(e) { // Save this file in the path to URL lookup table. lookupTable[key] = fileEntry.toURL(); callback(); }; writer.onerror = failCallback; var bb = new BlobBuilder(); bb.append(xhr.response); writer.write(bb.getBlob()); }, failCallback); }, failCallback); }); }); xhr.addEventListener('error', failCallback); xhr.send();
The two methods both generate a URL for assets after request, and give a URL to the target.
Create element
Talking about the second method, you may worry about the compatibility of different browsers. It uses lots of HTML5 new features which are not supported by all browsers. So we use createElement
to make game assets compatible for all browsers:
var res = document.createElement("image/audio/xxx") res.src = http://www.yourdomain.com
But this method has the same problem: not every element provides onload function. It’s easy for images; but for audio and video, we can only estimate by using functions like canplaythrough
.
2.1 How to manage countless assets?
A game has hundreds of images that need to be loaded and displayed when it starts up. How do you know when all images are ready? Did they all successfully load? When should the game actually start? The direct way is to write images into code. However, image names are easy to change, which makes maintenance difficult. Here is a way to change assets names without touching the code:
{ "assetRoot": "url/to/assets", "bundles": [ { "name": "unique bundle name", "contents": [ "relative/path/to/asset.jpg", "another/asset.mp3" ] }, "autoDownload": true } var gal = new GameAssetLoader("http://path.to/gal.manifest"); // Load the GAL. If manifest indicates autoDownload, this call will // start loading assets one by one. gal.init(function() { // Called when the library is initialized });
2.2 How to load in batch
Considering the large amounts of assets to be downloaded, which all finish up in the browser, we need to find a way to speed up assets downloading—using a range of domain names for assets hosting. Web browsers see domain names as separate servers (AAA.com, BBB.com, CCC.com, etc), which increases the number of connections for downloading. That’s the way to speed up batch downloading:
AssetManager.prototype.downloadAll = function(downloadCallback) { for (var i = 0; i < this.downloadQueue.length; i++) { var path = this.downloadQueue[i]; var img = new Image(); var that = this; img.addEventListener("load", function() { // coming soon }, false); img.addEventListener("error", function() { // coming soon }, false); img.src = path; } }
A progress bar is required to show downloading progress, so AssetManager
must be counted
function AssetManager() { this.successCount = 0; this.errorCount = 0; this.downloadQueue = []; } AssetManager.prototype.isDone = function() { return (this.downloadQueue.length == this.successCount + this.errorCount); } AssetManager.prototype.getProcess = function() { return (this.successCount + this.errorCount)/this.downloadQueue.length; }
It is also necessary to count success and failure of image download. Please note that the downloadAll
function has a parameter called downloadCallback
, which informs the function to start the game after download is completed.
img.addEventListener("load", function() { that.successCount += 1; if (that.isDone()) { downloadCallback(); } }, false); img.addEventListener("error", function() { that.errorCount += 1; if (that.isDone()) { downloadCallback(); } }, false
Download as necessary
Games usually have different levels from easy to difficult. There’s no need to download all assets at once, because not every player will complete all levels. So it is advisable to download as necessary. You can download the corresponding assets before the level begins; then download the next level assets. It increases game experience by reducing loading time and pressure on the server.
3 How to display a progress bar
A progress bar displays the loading process of game assets. In HTML5 language, progress
tag contains value and max property; that’s what we need to create process bar. The code for progress bar would be 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); }; };
4 How to store game assets
Image source: Search via Bigstockphoto
Game data is essential to a game’s lifespan. We inform users whether they played better or worse via data. We save the latest score and advocate to continue by comparison. This may make users addicted and keep them playing.
In HTML5 games, we use local storage to store game data. It is sustainable storage, which allows a website to store information in the browser locally and access the stored data later. This is a useful feature in game development because we can use it as a memory slot to save any game data locally in a web browser.
/** * synchronize request, return target(Json or other types) * provide retrieve method */ requestJson : function(_url, callBack){ //return result var result = {}; //cut second part of request url as key value of local storage //e.g. ***/***/data.js(.htmlor.xml) cut'data' as key var arr = _url.split("/"); var key = arr[arr.length - 1]; //judge local storage support if(window.localStorage && undefined != window.localStorage[key] && null != window.localStorage[key]){ //because localStorage only supports string, so the extracted data will be treated by Json or eval function result = eval("("+window.localStorage[key]+")"); } else { //use ajax request $.ajax({ url : _url, type : "get", async : false, success : function(data){ //return data is string // judge local storage support if(window.localStorage) window.localStorage.setItem(key, data); // extracted data will be treated by Json or eval function result = eval("("+data+")"); if(callBack)callBack(result); //TODO execute other logic //... } }); } return result; }
Fast loading HTML5 games showcase
We have discussed some game assets loading issues; now I’m going to show you some of the best HTML5 games on the market. You can play for yourself and see how fast they load.
Angry Birds HTML5 version
I know you have played this, but it is still number 1 on my list. Since Angry Birds created such a miracle in the game industry, the HTML5 version is also significant. I think it’s unnecessary to introduce the game anymore, just enjoy the game and see whether you can reach level 70 or not.
Runfield
This is another important HTML5 game. The game dev uses HTML5 canvas for the graphics. It was created to show how fast Firefox 4 can run an HTML5 game. Actually, it is really fast, not only refering to the fox, but also the game performance.
HTML5 Port for Quake II
Quake II is a must-have game in my list. It is an FPS game using WebGL, Canvas API and other HTML5 elements. Although it requires setting adjustments like sound, it is one of the best examples of a web-based HTML5 game. Check here if you want to play.
Emberwind
Emberwind is an HTML5 game using WebGL to accelerate 2D graphics. You can control a stubby gnome watchman who sails through the clouds on an ancient snow owl, deploying your cane of justice in the battle against gremlins and other nasties.
Warimals Cats VS Dogs
As the name implies, this is a war game between cats and dogs. You can choose to be cats or dogs. The aim is to build your own units and earn cookies to acquire the resources used for building even more units.
The Convergence
The Convergence uses HTML5 canvas for its graphics. Although the game picture is a little bit rough, it is still a cute single-player game. You can control the couple and bring them together by moving them in opposite directions.
Project Blaze Zero
Although this is a demo currently, it is an addictive HTML5 game already. It is an aircraft shooting game with dazzling scenery. You control an aircraft by keyboard to shoot enemies down.
Fieldrunners
Fieldrunners can be played on WebGL supported browsers such as Chrome, Firefox, Safari or IE9. It is an HTML5 strategic game in tower defense style. It has nice effects and graphics in which you need to place emplacements to defend yourself from incoming hordes.
Conclusion
The game industry is undergoing Flash to HTML5 evolution. Although Flash games still take the largest market share on desktops, HTML5 games are catching up, especially on mobile platforms. Compared to Flash game loading that packs all the assets in SWF files and requests functions in Flash Player; HTML5 games are free from third party plug-ins. It’s easier and faster to load game assets.
In this article, we have discussed the common problems and solutions in HTML5 game loading. I hope you now have a better understanding of this topic via the article. If you have any other nice tips or solutions for HTML5 game assets loading, please share with us in the comments.