|
In this second installment of a three-part series, seasoned console developers, formerly of Criterion, describe the process of moving to HTML5 development, covering the essentials, including timers and graphics. The first part of the series can be found here.
In Part 1 of this series, we introduced HTML5 for developing high quality games, giving an overview of what developers can expect when transitioning from other platforms. In this second article we talk about specific areas of functionality provided by HTML5 and related technologies, with particular focus on games.
In some cases there are multiple interfaces for a given subsystem, each with their own characteristics and availability. We discuss how these interfaces can be used to deliver a high quality gaming experience across a range of browsers and platforms, and talk about strategies we have adopted in implementing the Turbulenz game platform.
An example of the level of quality that can be achieved with well-optimized code using the latest browser APIs can be seen in this presentation of the Turbulenz Engine at WebGL Camp Europe.
Core Functionality
The JavaScript runtime environment has a collection of default global objects that are always present and mostly standardized across browsers. Here we describe those with a particular relevance to games. w3schools is a very popular and practical place to look for detailed JavaScript documentation but the Mozilla Developer Network is often regarded as more accurate and prescribing better practices.
Window
The window object represents the page that the code is running in. Browsers also define this object as the global object for the JavaScript context, and it becomes the implicit this when calling functions directly.
This global window object contains all other global objects, meaning that these two statements behave identically:
var r = window.Math.sqrt(4); // explicit var r = Math.sqrt(4); // implicit
We recommend using window explicitly when checking for the existence of global objects.
The window object provides some fundamental functionality to the application:
- Timers and intervals.
- With setTimeout/clearTimeout and setInterval/clearInterval.
- An interval can be used to execute the main game loop, by requesting that the browser invoke a callback function every 16 milliseconds, for example.
- setTimeout and setInterval both work with integer values of milliseconds, so there is no chance of getting a stable 60 frames per second using this API naively.
- Alerts and confirmations.
- With alert, confirm, and prompt.
- To show modal dialogs with information for the user or to request confirmation.
- Not recommended for frequent use, only for critical notifications.
- Screen information.
- Via the screen object.
- Minimal information about the desktop, its dimensions and color depth.
- Browser information.
- Using the navigator object.
- Information about the browser itself, browser name, version number, OS platform and the user agent string it uses when talking to your servers.
- Events
- With addEventListener and removeEventListener.
- For keyboard and mouse events among other things.
- HTML DOM
- The document object provides access to all HTML elements in a page.
- Your game will render into an HTML element so controlling its location, dimensions and behaviour may require some HTML element operations. Libraries such as jQuery take care of the different issues and incompatibilities between browsers and are often used instead of trying to manipulate elements manually.
- Logging
- The console object provides logging functionality. Although not supported initially, nowadays it either exists by default on the browser, or extensions exist to emulate it (for example Firebug includes an implementation for Firefox).
Math
The Math object provides scalar mathematical operations and constants -- possibly one of the most reliable features across browsers.
All mathematical operations and constants provide double precision floating point values. Basic math operations like addition also work with double precision if the arguments do not fit on a 32-bit signed integer.
JavaScript does not provide SIMD support or any kind of vector and matrix library. Turbulenz has its own vector math library optimized to reduce object allocation and memory usage by supporting a destination parameter on every operation and by using typed arrays. The physics demos, included in our SDK samples give a good idea of the level of performance that can be achieved in optimized JavaScript.

Date
The Date constructor creates Date objects that provide basic date and time functionality. This constructor also provides a useful method called now that returns the number of milliseconds since the Unix epoch, without the need to create a Date object. The resolution of the time information varies by browser and is generally between 4 milliseconds to 15 milliseconds.
XMLHttpRequest
This object provides support for making HTTP requests. These requests could be asking for a specific resource or may represent operations to execute on the server. Browsers use the HTTP protocol as the main form of communication between the client and the server.
Although the object XMLHttpRequest requires different creation code depending on the browser (generally Internet Explorer is different from the rest) it has become common functionality so only really ancient browsers will lack support for it. Code like the following can be used to create an XMLHTTPRequest:
var xhr;
if (window.XMLHttpRequest)
{
xhr = new window.XMLHttpRequest();
}
else if (window.ActiveXObject)
{
xhr = new window.ActiveXObject("Microsoft.XMLHTTP");
}
else
{
return false;
}
Despite its name, XMLHttpRequest can download any resource type, text or binary. This code demonstrates how to support loading binary data:
xhr.open("GET", url, true);
if (xhr.hasOwnProperty && xhr.hasOwnProperty("responseType"))
{
xhr.responseType = "arraybuffer";
}
else if (xhr.overrideMimeType)
{
xhr.overrideMimeType("text/plain; charset=x-user-defined");
}
else
{
xhr.setRequestHeader("Content-Type", "text/plain; charset=x-user-defined");
}
xhr.send(null);
You can then access the resource data:
var buffer;
if (xhr.responseType === "arraybuffer")
{
buffer = xhr.response;
}
else if (xhr.mozResponseArrayBuffer)
{
buffer = xhr.mozResponseArrayBuffer;
}
else
{
var text = xhr.responseText;
var numChars = text.length;
buffer = [];
buffer.length = numChars;
for (var i = 0; i < numChars; i += 1)
{
buffer[i] = (text.charCodeAt(i) & 0xff);
}
}
We will talk about resource loading in more detail in a later article.
|
This isn't correct. RequestAnimationFrame is using v-synch. It'll try to match the monitor's frequency, not a flat 60 FPS. Try changing your monitor's refresh rate and run a basic RequestAnimationFrame demo in Chrome using the Timeline tab. It'll show the default 60 and 30 FPS range, but the transparent "frame overhead" bars will match the v-synch. For example, for 75Hz the bars should be a bit lower than the 60 FPS line and the frame time should be about 13 ms instead of 16 ms.
"The expectation is that the user agent will run tasks from the animation task source at at a regular interval matching the display's refresh rate. Running tasks at a lower rate can result in animations not appearing smooth. Running tasks at a higher rate can cause extra computation to occur without a user-visible benefit."
This is from the w3c documentation (https://dvcs.w3.org/hg/webperf/raw-file/a43340fd9097/specs/RequestAnimat ionFrame/Overview.html). EDIT: I have no idea why but Gamasutra forces a space in RequestAnimationFrame in the address. Remove it if you wanna check the page.
"This API will take page visibility and the display's refresh rate into account to determine how many frames per second to allocate to the animation."
This is from Microsoft's site (http://ie.microsoft.com/testdrive/Graphics/RequestAnimationFrame/Default .html).
I'm fairly sure that Firefox doesn't synch. I guess that the thing to take away from this is, don't assume that it's going to be 60 FPS.
Actually HTML5 in general is still too shaky to even bother with it for mobile. Prepare for the time when it isn't, but it's just not good enough yet. Take for example Apple removing the trick that let you hardware accelerate animations by using the CSS rotation properties -- you just can't get the performance you need for any but the simplest cases.
"The repaint may occur up to 60 times per second for foreground tabs" and "we don't invoke the callbacks at a rate faster than 60Hz" are from other browser docs.
We do assume that by default the target rate would be 60 FPS, but of course we do code defensively, taking care of spikes or drops. The point is that with requestAnimationFrame it is perfectly possible to easily run at smooth 60 FPS and in many cases that will be the end result.
We did try to use setTimeout and setInterval and we never got smooth animations at 60 FPS.
Yes, mobile is lagging behind...
Is the 45 FPS being measured in real time or averaging? 45 is right between 30 and 60. The drop to 30 FPS would be correct since that's the "next step" in v-synch. To synch with a 60 Hz monitor, the FPS needs to be 60, 30, 15, 12, 10, etc... Though i have noticed myself that for some of my load tests Firefox and Chrome can sometimes stick with ~45 (measured in real time) which doesn't make much sense.