I recently decided to spend some time learning basic games programming using web technologies. I have started off as simply as possible, by cloning Pong. The source code for what I produced is available on Github, and you can play the game as it stands here. It’s for two human players, the left bat controls are W and S for up and down movement, D to serve, and the right bat controls are the up and down arrows for up and down movement, and the left arrow to serve. There’s a lot more polishing I could do, but the basic mechanics and scoring are there and seem to working reasonably well.
I’m not going to go into exhaustive detail about the development process, as nothing about it was particularly novel, but I do want to share some particular notes and observations.
Keyboard Input
Keyboard input on the web is all event based. You subscribe to notifications for when buttons are pressed and released. For a game like Pong though, with a game loop that updates the world on every timer tick, you’re usually interested in interrogating the state of various buttons. E.g. is the down button pressed, if so, move the right hand bat a certain amount. Fortunately, it’s easy enough to build a little class to wrap the HTML keyboard model in a more suitable API. This blogpost on nokarma.org provides a good explanation, accompanied by code, and I used a slightly modified version in my own game.
I discovered that one problem remains unsolved in keyboard input for web games, that of scan codes. The problem is this: games like to define sensible default controls, such as the using the WASD keys for movement in first person shooters. However, the WASD keys are only sensible defaults when you use the QWERTY keyboard layout. For a user with a different keyboard layout, they’ll be in other positions. When developing for the desktop, games developers get around this problem by using scan codes, rather than character codes. Scan codes are the underlying code that the keyboard hardware communicates to the computer, which the OS then maps to a character. While the W key may be mapped differently according to keyboard layout, its scan code should remain the same. Unfortunately, keyboard scan codes can differ across devices, which could result in cross compatibility problems if they were exposed to the web. An application that used PC compatible scan codes might break on a platform that used some other mapping.
How to best to solve the scan code problem is apparently still an open issue in web standards. While it’s somewhat outside my realm of expertise, if I was to offer a proposal it would be this: Create a new web standard that includes a default set of scan codes based on the codes contained in the USB standard. Browsers should then take responsibility for mapping from the scan codes of the underlying hardware to those in the stanard. Since almost everything nowadays uses USB, that would usually be straightforward. If the browser was unable to determine scan codes, due to the underlying platform not having them, or using some unknown mapping, the browser could provide an error condition to the JS code, allowing it fall back to some sensible default, such as using key codes instead.
Canvas vs SVG
One of the first choices I faced was whether to use Scalable Vector Graphics (SVG) or the Canvas element for my game’s graphics. The canvas element provides simple, immediate mode, drawing of 2D graphics using a procedural API. SVG is a markup language for vector graphics, akin to HTML, that provides retained mode graphics and high-level API for manipulating the DOM elements that represent lines, shapes, etc.
I was aware that almost all web games use canvas instead of SVG. Often, this seems to be because they are conversions of existing Flash or desktop games, and have been developed using a 2D API on those platforms. In my case however, I decided to use SVG. Since I didn’t need to do anything fancy, just get some simple white shapes on a back background, I didn’t see any need for the kind of pixel-level manipulation that Canvas provided. And being vector-based, SVG would give me resolution independence for free. I was also curious to see if there was any problems with using SVG that might also explain its relative unpopularity compared to canvas.
The first thing I discovered using SVG is that the language and API are not super-intuitive, and nor are they particularly well documented. There is a fair amount of reference material on the Mozilla and Microsoft Developer Networks, but I couldn’t many higher level tutorials, that pull together reference material to explain how best to animate elements or construct and manage an interface for a game or application. There’s enough out there to muddle your way through, but this relative dearth, combined with the fact that a DOM based API is rather less intuitive than one for 2D drawing , as used by canvas, means SVG has something of a steeper learning curve.
More worrying was the performance issues I encountered trying to get smooth animation of the bat and ball. I used Paul Irish’s requestAnimationFrame polyfill to handle animation. RequestAnimationFrame is a relative new JavaScript API that replaces manually created timing loops with a browser-managed and optimised callbacks. In theory, it should help you get nice and smooth, 60FPS animation, while also ensuring you don’t overwork the CPU or run down the battery on a mobile device. Unfortunately, despite the extreme simplicity of my game’s graphical requirements, just three of moving elements, all of them plain white rectangles, I found I didn’t get 60FPS animation in most browsers, even when running on the beefy, multi-core desktop machine I have in work. Instead, I seemed to get locked at 30FPS, presumably due to some vsync issue, which meant the animation of the ball in particular was much less smooth than I would have liked.
The only browser in which I was able to get reliable smooth animation was recent versions of Google Chrome, with a flag to enable hardware accelerated composition of all pages turned on. What’s odd is that both Firefox and IE9, two of the other browsers I tested with, supposedly have hardware accelerated rendering as well, but still could not hit 60FPS. While I can’t discount the possibility that there is some problem in my code that is dragging down the frame rate, as it stands I have to conclude that SVG is not optimised for very smooth animation in most available browsers. I don’t know if canvas would necessarily have performed better, but I intend to use it for the next game I develop, and see how it fares.
Next Steps
For my next trick, I’m thinking of trying to implement something more complex, such as a simple platform game. I’ve found quite a promising looking reference how to implement the engine, a JS/HTML conversion of that Infinite Mario game that did the rounds a while back. I didn’t realise it at the time, but apparently Infinite Mario was developed by Markus “Notch” Persson, of Minecraft fame. The code seems to be a fairly clean and understandable implementation of a minimal platform engine, so should be ideal to learn from.
Leave a Reply