Developer. Creative Geek. Composer.

HTML5 Platformer: the complete port of the XNA game to with EaselJS

After a couple of hours coding with JavaScript, I’ve finally finished porting the XNA 4.0 Platformer game sample to HTML5/Canvas with the help of the EaselJS gaming framework. This article will provide you the game and the story of some of the questions I’ve asked myself while coding it. If you’d like to know how the game works, simply read the JavaScript commented code available at the end of this article. Please note that my main goal was to better learn JavaScript by writing pure JS code (with no form of dependency to the DOM) and to write a cross-browsers working game and cross HTML5 compatible devices when possible also.

You can play the game directly inside this iframe (left & right arrows keys to move & W to jump). Just press the “Start” button to launch the download sequence and the game itself.

Or you can play with it in a separate window through this link: HTML5 Platformer

Note 1: the game has been first released in September 2011 with EaselJS 0.3.2 and has been updated for EaselJS 0.5 on 8/31/2012. You can now use requestAnimationFrame on the supported browsers to check if it’s boosting your performance or not. You just have to check/unckeck the above case to see in real time the results.

Note 2: this game has been tested with success under IE9/10, Chrome 21, Firefox 15, Opera 12, IE9 on WP7 Mango and iPad 2. At last, if you really want to play to the game, an hardware accelerated browser is highly recommended. I’ve got 60 FPS in IE10 in these conditions with an nVidia GT330m (Vaio Z) or with an integrated Intel HD4000.

At the end of this article, you’ll find the complete non-minified source code of this game inside a ZIP archive to download. I’ve tried to comment as much as possible the code to make it self-explicit. I hope you’ll learn some interesting things while reading it. If you have any feedbacks and/or suggestions on it, feel free to add it as a comment to this blog’s post.

I’ve also already written 2 articles covering the basics of the logic behind this game here:

Building the content manager

In the previous article, the download content manager wasn’t notifying the user of the progression of the download process which was very bad. Hopefully, adding this feature was pretty straight forward as you just have to have a tick() method to your object and update a text element on the stage to show the progression. You can find that by reading the ContentManager.js file.

Moreover, as my objective was to be compatible with all browsers, I’ve then encoded the music & audio elements both in MP3 and OGG. If you’d like to know more about this HTML5 Audio tag codecs story, I recommend you having a look to my colleague’s article: 5 Things You Need to Know to Start Using Video and Audio Today

Then, I’m downloading either the MP3 or OGG version using the following code:

I’ve spent more time trying to figure out what was the best way to preload or download the audio files before starting the game. In the case of the image elements, we can’t start the game at all if we don’t have downloaded all the images locally. Otherwise, trying to draw on the canvas with non-yet downloaded images will fail.

For the audio elements, my wish was to be sure to play it exactly when I will need it. If the player dies, I don’t want the sound to be played 2 sec after the fatal event. Unfortunately, there is no real way to do that properly in all browsers. The HTML5 Audio element has been made to stream the audio file and plays it as soon as enough data is available. We don’t have an event saying: “the audio file has been completely downloaded”. My idea was then to download a base64 version of the file using an XMLHTTPRequest and to pass it to the audio tag using <source src=”data:audio/mp3;base64,{base64_string}” /> for instance. But I didn’t have the time to test if this is working or not. If you have done some similar tests, I’m interested in the results.

So finally, the only thing I’m doing is calling the load() method to start loading as much data as possible before using it. But on slow network, this won’t prevent the case where the audio won’t be ready while the game asks it. This won’t generate an error, but the sound won’t be synchronized with the action.

Working around HTML5 Audio tag limitations

While coding and testing the game, I’ve found another issue using the HTML5 <audio> tag (I didn’t expect spending so much time on the audio part!). If the player takes several gems too quickly, the audio element associated to this event can’t handle it. For instance, at the beginning, when the player was taking 8 gems on the same line, I was able to hear only 1 to 3 times the sound “GemCollected.mp3/ogg”. This means that no sound was emitted for the 5 remaining gems.

And during the game, I’m cycling inside this array to be able to “simulate” multichannels sound. You can find this trick in the UpdateGems() method of Level.js:

/// <summary>
/// Animates each gem and checks to allows the player to collect them.
/// </summary>
Level.prototype.UpdateGems = function () {
for (var i = 0; i < this.Gems.length; i++) {
if (this.Gems[i].BoundingRectangle().Intersects(this.Hero.BoundingRectangle())) {
// We remove it from the drawing surface
this.levelStage.removeChild(this.Gems[i]);
this.Score += this.Gems[i].PointValue;
// We then remove it from the in memory array
this.Gems.splice(i, 1);
// And we finally play the gem collected sound using a multichannels trick
this.levelContentManager.gemCollected[audioGemIndex % 8].play();
audioGemIndex++;
}
}
};

I know this is not efficient at all. Maybe some Audio APIs will help us in the future to better handle the audio in our games. I know Mozilla and Google are working on some good suggestions but this is far from being mainstream for the moment.

In my gaming scenario, this leads to problems while handling the animations. We don’t have the guarantee of being call-backed every xxx milliseconds in our update logic. To avoid that, XNA provides 2 different separate loops: the drawing loop and the update loop. In my case, they are more or less merged. And this is where the problem is. For instance, if the elapsed time between 2 ticks is too important (due to the single threading processing), I could miss an update call that would prevent one of my enemies to hit testing properly its environment. This then leads to very weird results. This is why in some parts of the code, I’ve hard-coded the expected elapsed time to 17 milliseconds. In the XNA version, the elapsed time is compute and often equal to approximately 16 ms (60 FPS). The solution to better handle the update loop (the tick() method in EaselJS) could be to use some WebWorkers.

On the other side, the solution for HTML5 gaming to properly handle animations could come in the future with requestAnimationFrame(). This is now supported by Internet Explorer 10 and can be used since EaselJS 0.4. But there are some interesting debates around it on the web. If you’re interested in this topic, I suggest you reading these 3 articles:

1 is where the player will start the game, X is the exit, G is a Gem, # is a platform block, – is a passable block, C one of the monsters/enemies, etc.

So, if you’d like to add levels to the game, simply add a new text file and edit the level yourself with… Notepad! You’ll need also to modify the value of the numberOfLevel variable inside PlatformerGame.js.

Awesome tutorial. One thing I've noticed is that the entire screen moves within the browser container when you hold it with your finger and move up and down. Is there a way to get rid of this? (I'm using Phonegap + your code on WP7)

Thanks for this, it's really great! I noticed the levels backgrounds and order are randomised, you set an amount of levels you want, configure the platforms in the .txt files and the layered backgrounds are randomly created. What if I want to have a finite amount of levels, say 10, with specific backgrounds for each one, level1background.png, level2background.png, level3background.png and so on up to level 10. They should always run in that sequence from level 1-10, not in random order? Is that possible?