Friday, December 7, 2012

A Simple 3D Renderer in HTML5, Part 3

Architecting the Source Code

The next major issue to tackle was a basic architecture for the code.  Javascript is pretty liberal with global values and just running functions in place, as well as not caring whether things exist until the particular piece of code is actually executed.

http://www.wooga.com/2012/06/woogas-html5-adventure/

We spent some time reading through this excellent postmortem, as well as many of the links it provides.  Actually looking at the source code for the game provided some interesting insights for the code architecture.  The big one was to create a single global variable that would behave essentially like a namespace, though in reality is just holds a bunch of function pointers.  An entire class file would be encased in the following code:

( function()
{
    // All class code
} () /* Not only are we defining the function, but we're immediately executing it */
);


So when we actually get down to it, we are able to create individual files for each class in our code.  Each class is encapsulated in a function that's immediately run, so it effectively isolates all the local information about the class.  At the end of the function, the main class function is assigned to an appropriate member of the global namespace variable so it can be accessed anywhere.

For inheritance purposes, we still need to include the class files in a specific order (super classes before those that inherit from them).  When the class's prototype is assigned to a new instance of its superclass, that superclass function is executed immediately.  If it doesn't exist yet, the assignment won't work.

In this first go-around with Javascript classes, we ended up making all class members and functions public.  Javascript classes and inheritance are haphazard at best, and they are only able to represent public and private class members, not protected.  This made the code easier to read in many cases, but it also led to shortcutting some functionality since there was direct access to normally private or protected member variables.

A lot of information about object oriented programming in Javascript can be found here:

http://phrogz.net/js/classes/OOPinJS2.html

Later on, well after the completion of this project, we also stumbled upon this article:

http://msdn.microsoft.com/en-us/magazine/ff852808.aspx

The Game Loop and Performance

Back in the actual game, we looked at ways of creating a proper game loop.  There are a couple standard ways of doing this:

setTimeout()

This simply runs the requested function after the specified number of milliseconds have passed.  So for a game loop, you would write the loop function, and then as the last command, call setTimeout() with the loop function to run again.

setInterval()

This runs the requested function repeatedly, separated by the specified time interval in milliseconds.  This saves the trouble of needing to call the loop function again at the end, as the Javascript engine will do that itself.

We also ran across an updated way to handle the game loop, which we ended up using in the end.

http://paulirish.com/2011/requestanimationframe-for-smart-animating/

This is similar to setTimeout(), but it's more optimized for browsers that support it, especially mobile browsers.  If the browser doesn't support it, it falls back on setTimeout().

One big issue we had to contend with - and that we never fully solved - is that even with the best of intentions and optimizations in our code, we're still at the mercy of the particular Javascript engine for frame updates.  Sending off any of the timing functions allows for a good chance of them running again at the desired time, but not necessarily.  Chrome and iOS Safari run quite nicely, but Firefox tends to hitch periodically, especially if you have lots of other tabs open.  We do our best to limit the maximum frame delta, but this can still cause problems later (check out the upcoming audio post).

We do use one big trick to try to squeeze more performance out of the browsers that support it:

http://developers.facebook.com/html5/blog/post/2012/04/17/making-a-speedy-html5-game/

Specifically, wrap the overall HTML document object hierarchy with a 3D CSS3 transformation that does nothing (like scale3d: 1, 1, 1).  This will request that some browsers (specifically mobile ones) attempt to use their graphics chip to speed up rendering.

Coming up, JSLint, mobile device accelerometer support, and audio.

No comments:

Post a Comment