Rotating Tower Foundations

Mon, Oct 28, 2013

Yesterday I posted my rotating tower platform game demo along with a vague promise I’d write up some ‘how to’ articles…

At its heart, the demo is just a slightly more complex version of my earlier tiny platformer. The biggest changes are in how its rendered (obviously) and in its more advanced collision detection…

… but before I get into depth on those topics (upcoming articles) I will give a brief run down of the basic game foundations, including

  • The code structure
  • The game loop
  • Loading assets
  • The module pattern, constants and variables

These are common topics across most HTML5 games, and they should be familiar to anyone who has read any of my previous game articles. If you wanted to build a real production-ready game you would most likely use a framework to provide some of these base foundations, but for prototypes and simple games it’s easy enough to build them yourself.


The Code Structure

The javascript code for this demo is broken into 2 parts:

  • common.js - general purpose game foundation code
  • tower.js - rotating-tower specific game code

In addition, a small 3rd party component, fpsmeter.js is used for the FPS counter.

Much of common.js has been extracted from previous games and provides:

  • Dom.* - Simple DOM helpers
  • Game.run() - A fixed-timestep game loop
  • Game.Canvas.* - HTML5 <canvas> helpers
  • Game.Load.* - helpers for loading assets
  • Game.Math.* - math helpers


The Game Loop

Common to all my previous games is a simple fixed timestep game loop that will use requestAnimationFrame to provide a render() loop, while maintaining an independent fixed timestep update() loop:

var Game = {

  run: function(options) {

    var now,
        dt       = 0,
        last     = Game.Math.timestamp(),
        step     = 1/options.fps,
        update   = options.update,
        render   = options.render;

    function frame() {
      now = Game.Math.timestamp();
      dt = dt + Math.min(1, (now - last) / 1000);
      while(dt > step) {
        dt = dt - step;
        update(step);
      }
      render(dt);
      last = now;
      requestAnimationFrame(frame, options.canvas);
    }

    frame();
  },

  ...


Allowing the main tower.js module to run the game by simply providing 2 methods, one to update() and another to render():

function update(dt) {
  ...
}

function render(dt) {
  ...
}

Game.run({
  fps: 60,
  update: update,
  render: render
});


Loading Assets

However, before we can start our game loop we must load our graphics and our JSON level data.

The common.js module provides 2 helper methods for this:

  • Game.Load.images - load multiple images and callback when complete
  • Game.Load.json - makes a very simple AJAX call to load and parse JSON data

In our main tower.js module we can now load our images, followed by our JSON level data, and finally run the game loop once all assets have been loaded:

function setup(images, level) {
  ...
}

function update(dt) {
  ...
}

function render(dt) {
  ...
}

Game.Load.images(["player", "monster", "ladder"], function(images) {
  Game.Load.json("levels/demo", function(level) {
    setup(images, level);
    Game.run({
      fps: 60,
      update: update,
      render: render
    });
  });
});


The Module Pattern, Constants and Variables

While the common.js module exposes its helpers using traditional javascript objects as namespaces, the main tower.js module uses the module pattern to keep its implementation private.

Since this game is simple, we can benefit from keeping it in a single module, allowing classes to share common CONSTANTS and give global (within the module) access to helpful utility methods and shared objects (like the camera, player, and renderer)

Removing most of the code reveals the structure of the module:

(function() { // private module pattern

  'use strict'

  //===================================
  // CONSTANTS
  //===================================

  var FPS    = 60,
      WIDTH  = 720,
      HEIGHT = 540,
      ...

  //===================================
  // VARIABLES
  //===================================

  var tower,
      monsters,
      camera,
      player,
      renderer;

  //===================================
  // UTILITY METHODS
  //===================================

  function normalizex(x)              { ... }
  function normalizeColumn(col)       { ... }
  ...

  //===================================
  // GAME - SETUP/UPDATE/RENDER
  //===================================

  function setup(images, level) {
    tower    = new Tower(level);
    monsters = new Monsters(level);
    player   = new Player();
    camera   = new Camera();
    renderer = new Renderer(images);
  }

  function update(dt) {
    ...
  }

  function render(dt) {
    ...
  }

  function run() {
    Game.Load.images(IMAGES, function(images) {  // load our images ...
      Game.Load.json("demo", function(level) {   // ... then our level data
        setup(images, level);                    // ... setup our entities
        Game.run({                               // ... and start the game loop
          fps: FPS,
          update: update,
          render: render
        });
      });
    });
  }

  //===================================
  // GAME CLASSES
  //===================================

  var Tower = Class.create({
    ...
  });

  var Player = Class.create({
    ...
  });

  var Monsters = Class.create({
    ...
  });

  var Monster = Class.create({
    ...
  });

  var Camera = Class.create({
    ...
  });

  var Renderer = Class.create({
    ...
  });

  //===================================
  // LET'S GO!
  //===================================

  run();


})();


Next Time…

That was a very brief tour through the code structure of the rotating tower demo, looking at the game loop, the asset loaders, and the module pattern of the main game code. This is common infrastructure required by all games and is a repeat of some of the work from my previous games

Next time we will take a look at how a 2D platform game can be rendered as a (simulated) 3D rotating tower.

In the mean time, you can…


Enjoy!