Over the holiday break, I decided to spend some time on an interesting project. I wanted to remake one of my Flash games on a different platform. Don’t get me wrong. I thoroughly enjoy developing for Flash Player and AIR. I just wanted to try something new. A little cross-training to give me a fresh perspective. Over the last several months, this idea has been stewing a bit. I considered things like going all out and learning Python to try pygame, but that seemed like it might take up a little too much of my time. Ultimately, it seemed that HTML5 and JavaScript were probably the best way to go due to JavaScript and ActionScript having a similar language heritage.
The result of my efforts over the last couple of weeks is Gridshock in HTML5:
Picking a Game Library
Not too long ago, I discovered Crafty, a JavaScript library for games. The documentation describes components that are simple and to the point, and I felt like I already had a pretty good understanding of the library after a quick glance over the APIs. As of this writing, Crafty is still at version 0.1, so it’s not exactly mature. Let’s be honest, though, most JavaScript game libraries are brand new anyway. I attribute that to IE9 finally getting Canvas support. Anyway, like I mentioned earlier, I wanted to branch out and try game development outside of Flash, but I have other projects I need to get to soon, so I needed to pick a library as a foundation so that I didn’t get stuck on low-level architecture decisions. Crafty seemed like the right choice for me.
Crafty, a JavaScript game library
Very recently, Grant Skinner released Easel, a JavaScript graphics library based on the display list in Flash. This, of course, should be an obvious choice for the conversion of a Flash game. It’s hard to explain what made me stick with Crafty (which I discovered first). I think my decision ultimately came down to the fact that I wanted to work with a library created by someone from outside the Flash community. More chances to learn new techniques. That said, if I ever make another JavaScript game, Easel will be a top contender so that I can make comparisons and decide on which one I like best.
Other Libraries
Playtomic is way ahead of the curve. In addition to Flash support, they offer a leaderboards API for HTML5 too. I was pretty excited to discover them one afternoon because, otherwise, I would have only stored high scores locally. I am not a backend guy, and I have zero desire to set up high scores on my server. Mochi Leaderboards are great for Flash, and it appears that Playtomic is a great way to go for HTML5. Please note, however, that Playtomic is currently only free for one casual game. However, the flat-rate prices don’t seem too steep.
humaneDate() is a nice little JavaScript function for converting dates to something that’s a bit more readable (like “2 hours ago”). Playtomic provides dates like this automatically, but I needed humaneDate() for the local player’s high scores that I store for convenience. Unfortunately, the written format is a little different between the two. The non-humane date returned by Playtomic isn’t an exact time, either, so I can’t accurately reuse that with humaneDate(). If this were more than just a learning exercise, I would have modified humaneDate() to match Playtomic’s output.
I ported GTween to JavaScript. It’s my tween library of choice in Flash, and the Flash version of Gridshock uses it, so I figured it would save more time if I made GTween work in JavaScript instead of converting to an existing JavaScript tween library. Edit: gtween.js on Github.
Modernizr, a JavaScript feature detection library
Modernizr offers an excellent way to detect modern browser capabilities and display alternate content if the current browser doesn’t stack up. If you load Gridshock in an older browser, you’ll see a message that suggests upgrading and links to the Flash version. Modernizr helped me detect if JavaScript is enabled, and whether the browser supports HTML5 Audio, Canvas, and Local Storage. I also included some manual checks for JavaScript features I needed, like defineProperty(), which is JavaScript’s version of getters and setters.
From ActionScript to JavaScript
JavaScript is a different beast than ActionScript 3, as we all know. They may have originated in the same place, but there’s a certain extra comfort AS3 provides with classes, strong typing, and the automatic compiling during development in Flash Builder.
Still, though, there’s something kind of elegant about JavaScript. It’s looser, but I thoroughly enjoy playing with closures and things like that. In fact, after I spent a few months a couple years ago writing a wrapper for my Flash charts to make them available to YUI JavaScript developers, I started using closures in ActionScript quite a bit more. This previous experience made conversion of Gridshock to JavaScript a lot easier.
You dawg, I heard you like functions, so I put a function in your function…
Crafty has a component system that creates “entities” with mix-ins. If you add the 2D component, the entity will have x, y, w, and h properties, along with some methods for working with them. If you add the canvas component, the entity will draw itself on Crafty’s stage. If, on the other hand, you add the DOM component, it will add an image to the page’s DOM on top of the canvas.
I quickly started creating new components for Crafty. One for alpha was the most obvious because I like fading things in and out (like the lights on the grid). Another, that I called “autoz”, I created to automatically manage depth. Similar to older versions of Flash, I discovered that I was forced to manage depth manually with Crafty. This simple component kept me from tearing my hair out when I discovered that the order of drawn entities wasn’t always consistent.
Both of those additions mixed-in with existing components right away, and I found myself enjoying this approach over inheritance. In my experience, inheritance in JavaScript can be a pain. It’s certainly doable. I liked YUI’s implementation back when I was using it. However, I found myself embracing composition and mix-ins more easily than I would in ActionScript 3. I think a lot of developers moving to JavaScript from a language with classes should take a moment to consider living without inheritance, if only to learn something new from the experience.
HTML5 features
While creating Gridshock, I worked with three features of HTML5—Canvas, and Local Storage, and Audio.
Canvas
I actually did very little coding directly with Canvas. Crafty had most of that covered. There was a bug or two in Crafty that I needed to workaround, but my tweaks were minimal.
To be honest, I think the Canvas drawing implementation in Crafty is a nice start, but it’s certainly not the most optimal. I noticed my laptop’s fans powering up a lot whenever I was testing Gridshock. Crafty draws sprites too often. There’s no invalidation system to draw all changes in one go on each frame (yes, Crafty has an enterframe event similar to the one in Flash). Instead, if I change the x position of a sprite, it draws to the Canvas immediately. Then, if I change the y position on the next line, it draws again. For my needs, with a simple casual game like Gridshock, that’s fine. However, I hope that the author will look into improving this in the future.
I didn’t run into any cross-browser issues with Canvas.
Local Storage
Local Storage worked like a charm across all the browsers I tested on. If you’re unfamiliar, Local Storage is similar to Flash’s SharedObject. However, it’s a bit more primitive. Everything is stored as a string. To store complex objects, you need to encode to JSON. Not a difficult workaround there, and that’s probably what the HTML5 working group intends.
I used Local Storage to store a list of the player’s own high scores, and I store the game’s settings with it as well (sound effects and patterns mode, as found on the Options screen).
The only cross-browser issue I ran into here was that Date objects weren’t encoded to JSON the same way in some browsers. That’s the JSON implementation, though, not Local Storage. Regardless, the workaround was easy. I just stored the UTC date string instead. That’s easy to parse with new Date(utcString)
.
Audio
Oh man, getting audio to work cross-browser is a major pain. Canvas and Local Storage are both pretty much fully baked in all browsers at this point, from my experience. The audio situation, on the other hand, is still very raw.
I did not have a protective suit like this. Photo by vacantfever.
Crafty has a very simple audio API that wraps HTML5 Audio, and it seems to work consistently. However, it didn’t meet my needs. I need to play a sound effect every time the player clicks on the light grid. A single Audio instance can only play itself once, and you must wait until it is done playing until you can play it again. That means I have to store multiple instances of the same audio file so that I can loop through and pick one that isn’t currently playing.
That’s easy enough. Annoying coming from Flash’s sound capabilities, but easy enough. However, I still ran into other issues. Chrome stops being able to play certain sounds after a while. They’ll play over and over without issue for a couple of minutes, but then I suddenly hear nothing when I try to play them again (while the other sounds continue working). Opera has loud clicks at the end of every sound. Firefox won’t loop audio (that’s why I didn’t include a music track). IE won’t let me create an Audio object and play it right away (or, at least, once a load event fires). Some browsers don’t play the click sound on buttons. It seemed that every variation of the Audio APIs that I tried failed in at least one browser in some subtle, or not so subtle, way. The code I settled on seems to be the most stable, but audio is often delayed before it gets played, and Chrome still stops playing certain sounds sometimes.
In short, Audio isn’t ready for prime-time. The two days I spent hammering on it were extremely frustrating. There’s probably some magical combination of APIs that will fit my use-case in a cross-browser way, but I didn’t find it.
Other Challenges
A couple other things weren’t straightforward.
Preloader
A preloader is easy in Flash. A game SWF is distributed on its own as a single file, so you just have to display some animation on the first frame until the rest of the frames load. In HTML, I had to structure my page so that JavaScript files loaded in the right order, and I had to extend Crafty to preload any images I needed for the sprites before I started the game. Yes, technically the game would run fine if the images loaded as they were needed, but that sort of ruins the experience for a game. Fine for a document, not so much when games need more real-time interaction that could be affected by assets that display too late.
UI Controls
Not super easy, especially when trying to make something work with Crafty. I ended up making my own button and toggle switch components within Crafty’s architecture. When I realized the work required to manually draw a list component for my high scores, though, I felt like gagging. I ended up putting a scrolling div on the DOM with a skinned table inside. This approach felt hacky, since I integrated with Crafty a lot more when I made the other two controls. However, it got the job done. I think if I gave myself some more time, I could have built the list a bit more elegantly and had a nice minimum set of UI controls that could be reused across games. Maybe I’ll explore this more later if I decide to make another HTML5 game.
After seeing switches like this in iOS, I often choose them over checkboxes.
Parting Thoughts
I enjoyed this little field trip into the land of HTML5. Though I intend to continue making games in Flash, it provided me with a new perspective, and I’m happy to know that I can return to HTML5 with confidence at some point in the future, if I need or want to do that.
For instance, I may be interested in converting my AIR mobile games to an HTML5 mobile framework, like Phonegap, to take advantage of the greater native integration. For instance, I can’t release free, ad-supported versions of my games with AIR mobile because there’s no ad provider that works with the runtime. That’s been very frustrating for me. While I enjoy AIR on Android for easy porting from desktop to mobile, man, it feels limited sometimes.
Anyway, HTML5 feel like it’s nearly ready for casual web games. Currently, there seem to be two reasons to wait a bit longer:
-
As I discovered, the Audio support in browsers needs work. A lot of it. Audio is important in games, so this is a big one.
-
Most casual game players are regular people, and they may be stuck on IE at work, or maybe they haven’t yet upgraded at home. Once IE9 is released and has gained a critical mass among IE installations, then I think games may be ready to run on HTML5.
Of course, that doesn’t account for distribution. I don’t expect the HTML5 version of Gridshock to end up on portals. Unlike the Flash version’s single SWF that can easily be uploaded anywhere, the HTML5 version involves an HTML file, a folder of images (which could be put on a single sprite sheet, but that’s kind of a pain without an automated tool), a couple of minified JavaScript files, and a minified CSS file. The HTML, JavaScript, and CSS could be combined, I suppose. However, the images are a bit tougher. I guess if it were a giant sprite sheet converted to a data URI, that would get everything into the single HTML file. Definitely something that game developers and portals will have to think about for anyone who wants web games to move from Flash to HTML5. Unless a dozen portals email me tomorrow to say that they want to feature this version of Gridshock on their sites, I’m not worrying about distribution yet.
Enjoy the game? You can play Gridshock on iPhone and Android. By purchasing the apps and supporting me, you’ll be able to see more in-depth looks at my game development projects, like this one.