Wintellect Blogs

The Cascade Game: From Silverlight to HTML5 (part 2)

15 Sep , 2011  

Game Loops and Timer-Based Animations

Previous Entries in Series

My next short term goal was to get some of spheres to show and animate. And that was going to entail using HTML5’s new <canvas> tag. I saw two ways of going about animating the spheres. The first way would be to manually draw the spheres on a single canvas and animate them by clearing the canvas and redrawing at every game cycle. The second way would be to create a separate canvas for each sphere and use CSS3 to animate them. The latter method would allow the GPU to help accelerate the graphics processing and seems to be the best choice for animations of this type in HTML5 (see this blog entry from Jeff Prosise).

In the Silverlight version of this game, I created several classes that managed the spheres, levels, scores, and whatnot. I wanted to mimic that structure as closely as possible in Javascript to make the conversion less painful. Since Javascript doesn’t really have any type-safety without writing a lot of support code, I knew this task was going to be a bit frustrating until I became re-acclimated to Javascript again (I confess, it’s been some time).

As in the Silverlight version of the game, I’m going to rely on a game timer to run my main game loop.  In Silverlight I used a simple DispatcherTimer to control the animation.  In HTML/Javascript, there aren’t too many choices: setInterval and setTimeout.  The setInterval method will execute a callback at a specified interval, while setTimeout will execute a callback in a specified time span.  In essence, setInterval will continue running the callback while setTimeout will run it once.  In the setTimeout methodology, the callback will usually call setTimeout again in order to keep the animation timer going.  Both these animation techniques have been in use for a long time…and they both suffer from performance issues.  Namely, they continually run whether or not the tab containing the HTML page is visible or not.

The solution in HTML5 is to use the new requestAnimationFrame method.  However, this API is still in the development stages and is not supported by all modern browsers (once again Internet Explorer 9.0 falls behind, but the API is supported in IE 10).  Paul Irish has a very good explanation and a workable fallback routine that I will be using for Cascade Html5.

The animation heart of Cascade looks essentially like most traditional game loops, like this:

   1: window.requestAnimFrame = (function () {
   2:     return window.requestAnimimationFrame ||
   3:            window.webkitRequestAnimationFrame ||
   4:            window.mozRequestAnimationFrame ||
   5:            window.oRequestAnimationFrame ||
   6:            window.msRequestAnimationFrame ||
   7:            function (callback, element) {
   8:                 window.setTimeout(callback, 1000 / 60);
   9:            };
  10:      })();
  12: function startLevel(level) {
  13:     instantiateSpheres(level.maximumSpheres);
  15:     startTimer();
  16: }
  18: function startTimer() {
  19:     requestAnimFrame(startTimer);
  20:     gameLoop();
  21: }

The startLevel() function is called by the main application and dynamically creates the game resources (i.e. the spheres) for a particular game level before starting the game timer.

The gameLoop() function is the routine that animates each sphere in relation to the others.   This method is responsible for two things during each animation cycle: 1) move each sphere, and 2) check for collisions with an explosion.  I’m using a rather basic loop to iterate through all the spheres and avoiding double-checking collisions.  Here’s the code:

   1: function gameLoop() {
   2:     // move and check for collisions.
   3:     for (var i = 0; i < _spheres.length; i++) {
   4:         _spheres[i].move();
   6:         for (var j = i + 1; j < _spheres.length; j++) {
   7:             if (_spheres[i].isColliding(_spheres[j])) {
   8:                 if (_spheres[i].isExploding) { _spheres[j].explode(_spheres[i]); }
   9:                 if (_spheres[j].isExploding) { _spheres[i].explode(_spheres[j]); }
  11:                 if (USE_COLLISIONS)
  12:                     _spheres[i].collide(_spheres[j]);
  13:             }
  14:         }
  15:     }
  16: }

The USE_COLLISIONS variable is a simple boolean that I can use to globally turn off the rudimentary physics in the game.  The sphere class encapsulates the logic for all movement and collision detection.  The gameLoop() routine simply tells to each sphere to move itself and then determine if they collide with each other.  Remember the gameLoop() routine should be running approximately 60 times every second.

So far, the animations are proceeding exactly as they did in the Silverlight version since both rely on a clock tick to perform their calculations.  At this point, I was quite happy with the migration from Silverlight since I was able to take the C# code and only slightly modify it to Javascript.  For example, here the same game loop in C#:

   1: void OnTimerTick(object sender, EventArgs e)
   2: {
   3:     // Move and check for collisions.
   4:     for (int i = 0; i < spheres.Count; i++)
   5:     {
   6:         spheres[i].Move();
   8:         for (int j = i + 1; j < spheres.Count; j++)
   9:         {
  10:             if (spheres[i].IsColliding(spheres[j]))
  11:             {
  12:                 if (spheres[i].IsExploding) { spheres[j].Explode(spheres[i]); }
  13:                 if (spheres[j].IsExploding) { spheres[i].Explode(spheres[j]); }
  15:                 if (!this.IsCollisionOff)
  16:                     spheres[i].Collide(spheres[j]);
  17:             }
  18:         }
  19:     }
  20: }

Some of the smaller problems I incurred during this portion of the conversion was color manipulation, points, and vectors.  Obviously, Silverlight and .Net in general has strong color manipulation capabilities; Javascript, not so much.  I ended up passing colors around as arrays of 4 bytes (R, G, B, and A) for easier manipulation.  The other issues stemmed from core libraries available in C# that deal with points and vectors but simply don’t exist at all in Javascript.  While I may have been able to find some external libraries for these pieces, they weren’t too difficult to reproduce.

However, I’m a bit concerned with next piece of the conversion: the explosions.  I used Silverlight’s key-frame animations in storyboards along with data-binding to easily create animations that explode the sphere to a multiple of its initial radius, pause for moment, and then contract to nothingness.  I’m anticipating issues attempting to use CSS3 and HTML5 to provide the same ease of animation development.  However, that’s a topic for the next part of this blog series.