Skip to content

2. Architecture and framework

Siorki edited this page Oct 19, 2014 · 8 revisions

Pest Control : Weasels

  1. Genesis. From the idea to the design
  2. Architecture and framework
  3. Critter tasks and animation
  4. Level design and display
  5. Minification and size constraints

Framework and high-level design

13kb does not leave much room for one of the usual frameworks. Sure, they take care of the technical layers, giving you the opportunity to focus on your game intrinsics only. However, this comes at the expense of some precious (kilo)bytes. In such a constrained environment, you are usually better off implementing everything yourself.

My background is from C++ environments, which drives me to think every project as an object-oriented design. Javascript is no exception to that rule, thus I have put together a set of core classes that implement a basic MVC pattern. They are pretty generic and get reused - and adapted - in quite every game project I develop :

  • Controls : the Controller in MVC. It receives browser events (only mouse events for PC:W, however the baseline handles keyboard too) and interprets them within the game context. Player actions are then sent as commands to the World.
  • Game : application class. Handles initialisation, game loops and hosts a state machine managing transitions between game screens (main menu, level intro, ingame, level completed).
  • Renderer : the View in MVC, called by the rendering loop. It is in charge of displaying on both canvases (playfield and overlay, see §4.)
  • SoundManager : detects sound API support by the device and browser, initializes module and sfx accordingly, and features entry points to start playing sounds. This instance uses the Audio element and only offers simple start/stop controls. Implementations using WebAudio require a sound loop to adjust at each frame.
  • World : the Model in MVC, sometimes going by a different name. Handles all details of the ingame activity, such as current level, play area, critters and traps.

Once completed, they handle most of the game logic. The source code features only three extra classes :

  • Critter : implementation of individual critter behavior, including a state machine for the current activity.
  • PlayField : the background scenery, that gets altered by mine explosions and built stairs. Handles procedural drawing of the levels and collision detection.
  • CPlayer : SoundBox module player implementation. Original is from Marcus Geelnard, this one is optimized for size by cutting all features not used in the main song.
  • The module and sfx data occupy an extra .js file, yet no class is defined for them.
  • The original design also featured a LevelLoader class, which contained nothing but the levels and an accessor to them. It was merged into World as part of the last byte-saving run.

Screenshot

Game loops

The game loop is the core mechanism used to process asynchronous events (user input), animate the playfield and render graphics. Javascript provides two sets of instructions useful to implement a game loop : setTimeout() / setInterval() and requestAnimationFrame()

setTimeout() and setInterval() attempt to keep a constant delay between two calls (for setTimeout(), you need you call it at the beginning of your loop, not at the end). You can thus expect the loop function to be invoked at a regular steps, and assume in your implementation that all steps are equivalent. However, if the call contains heavy processing, or the hardware lacks power, performances will degrade accordingly. Players with low-end hardware will run the game at a much lower speed than intended.

setTimeout animation

requestAnimationFrame() will call the loop function as often as needed and possible, without exceeding a desirable framerate usually set at 60 FPS. It also scales down when the page is hidden, not to trigger refresh calls if the application graphics are not shown on screen. A mechanism is provided to retrieve the actual delay since the last frame, which in turn should be used to maintain a constant animation speed : a character walking at a fixed pace will more by a distance proportional to the elapsed time. While it requires the game innards to support this variable step, this technique lets the game runs at a constant speed, no matter the hardware. The actual display may show a lower framerate on less powerful machines, and appear jaggy, yet the character will cover a distance in the same time as with a top notch CPU.

requestAnimationFrame animation

Most articles on the topic recommend the use of requestAnimationFrame(), only reverting to setInterval() when the former is not present, and dealing with the variable step. I instead chose to follow the advice of another fine piece of writing. I earlier mentioned game loops, emphasizing the plural. Yes, the article recommends separating concerns :

  • the main loop (for a lack of a more creative name ...) handles all game logic : critters life - and death - and user input are processed there. This loop clocks a constant 25 FPS (was initially 50 but the weasels were moving too fast, I first put them to rest every other cycle before realizing I could simply halve the framerate) through the use of setInterval().

  • the rendering loop performs only canvas rendering and nothing else. As far as the model is concerned, this is a read-only operation that is fueled by requestAnimationFrame().

This gives me the best of both worlds : no need to handle variable steps, which can prove difficult when dealing with boundary conditions : if the page is shown again after being hidden for a while, should the characters jump the equivalent of one minute of motion ? If the CPU is not up to the task, requestAnimationFrame() will get a lower priority : the game core will continue to run as fast as possible, while the rendering loop will throttle down.


  1. Genesis. From the idea to the design
  2. Architecture and framework
  3. Critter tasks and animation
  4. Level design and display
  5. Minification and size constraints