Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / CSS

Tom's Halls - A JavaScript Platform Game Engine

4.91/5 (12 votes)
8 Apr 2010CPOL8 min read 1   671  
A 2D platform game engine using JavaScript DOM manipulation and CSS
TomsHalls_v2.jpg

Version 3.0, April 2010

Version 3.0 of Tom's Halls basically "completes" the game - literally, as there is now a game-won sequence. Game-wise the playing area has been increased to 41 screens, various new retro sprites make an appearance, and many of the existing screens have been tweaked and revised.

From a technical point of view, I took the time to give the "application" a complete optimisation overhaul:

  • Previously there existed numerous global variables and functions. Almost everything has now been consolidated into a single object literal named game. The Viewport, which previously was an instantiatable "class", is now a property of game with its own sub-properties. I decided that as there was only ever going to be one game and one Viewport of fixed dimensions, I might as well optimise and simplify things accordingly. I considered making the instantiatable Player a property of game as well, but chose not to as it is large and complicated and would have made the game object unmanageable.
  • Various previously configurable properties such as the width and height of Player and "constants" such as "forceLeft" are now hard-coded for speed. This eliminated a very large number of object traversals and sped things up noticeably. This is at the expense of some readability of the code. For example, "+ this.player.width" gives much more indication of the intention than "+ 32", but needs must.
  • Numerous unnecessary consecutive object traversals were eliminated with local caching, e.g. var v = this.game.viewport;
  • Previously my setInterval calls were doing implicit evals, e.g.
    JavaScript
    gLoopInterval = setInterval('loop()', gLoopIntervalMs);

    This has now become:

    JavaScript
    this.loopInterval = setInterval(function () { game.loop(); }, 12);

Eternal thanks to Douglas Crockford's http://jslint.com/ tool, which helped me a lot this time round in making things robust and efficient.

Browser Behaviour

On my mid-specced notebook PC, Tom's Halls runs smoothly in Firefox 3.6, Chrome 4.1, and Opera 9.62. It is EXTREMELY slow in IE8, far slower than IE7. I have not yet tested v3.0 on Mac.

TomsHalls_v2.jpg

Version 2.0, November 2008

After much harassment from friends to extend Tom's Halls into a more fully-fledged game, I finally found the time to do so. The result is Tom's Halls v2.0 which you can play online at www.tomshalls.net. You can now explore a multitude of dastardly rooms awash with terminally geeky humour and private in-jokes. Be warned!

Technical Changes and Improvements

  • Improved Player physics, collision detection, and sprite interaction algorithms
  • Added collectable items. Each screen script adds its collectable items by assigning globally unique IDs to each, which if not already present are added into the Viewport's collectables array, and on each screen entry if not yet collected are added into the Element array and thus displayed.
  • Optimized all loops, array lookups, etc.
  • Tweaked the overall "object model" somewhat and rearranged methods.
  • Improved the graphics, while retaining the quasi-8-bit quaintness.
  • Preloaded images.
  • Minimized the number of inline styles applied against Elements by using the CSS to define width and height where possible, e.g. where no tiling of a background image or sprite size changing is involved.
  • Replaced all InnerHTML usages in the info panel area with proper DOM approaches.

Observations with Chrome

Since the launch of the original, the Chrome browser has appeared on the scene. My findings with Chrome were much like those with Opera during development of the original: Chrome's optimizations, apparently and unsurprisingly, are not towards this type of application. Compared to Firefox and IE7, the DOM access is painfully slow and erratic. Chrome also has an annoying habit of freezing up some seconds after first loading the page and then apparently reloading the page or rebuilding the DOM, effectively resetting your game. Finally, using dynamic opacity on even a single small sprite on the screen drastically slows down the entire screen. For this reason I canned my original approach to the flashing collectables which had them fading smoothly in and out, opting instead for an 80's-style flashing every half second.

Screenshot - TomsHallsGame

Introduction

Tom's Halls is / was a learning project to develop a simple 2D platform game engine using JavaScript DOM manipulation and CSS. My main intention was to upskill on the following:

  • JavaScript, especially prototypal inheritance
  • DOM manipulation
  • W3C standards-based markup

I chose to develop the code from the ground up rather than building upon third party libraries such as Prototype, partly for reasons of learning how these libraries actually do their thing, and partly to avoid their bloat factor and the sometimes reported risk of them not always playing nicely alongside other code.

Use the DOM, Luke

DOM div elements are used to represent both static room items such as walls and ladders, and also moving sprites such as the player and "devil player" monsters. Animation is achieved by modifying the left and top properties of a div's style attribute and its className attribute when the sprite changes direction.

My original approach to collision detection was to check each DOM element's position and dimensions directly by interrogating the left, top, width and height properties of its style attribute. However, this proved to be much too slow, especially when the game area became very complicated, partly because under XHTML Strict non-zero property values must include the unit (e.g. 200px), and I had to use clumsy string operations to strip away the unit and then apply maths to the remaining value.

I therefore chose to create an Element class which "wraps" a DOM element and when instantiated stores its position, dimensions, and other useful properties, and adds its underlying DOM element property to the viewport. Each Element also contains an array of child Elements, the top-most Element being the Viewport, which is itself a class which "inherits" from Element. Collision detection is achieved by iterating through the child Elements of the Viewport and testing each Element of the "solidity type" of interest, e.g. solid (walls, floor etc.) or climbable (ladders, ropes etc.).

Game Loop

A 15ms interval is used to call the main game loop, which does the following:

  • Moves sprites
  • Checks if the player is zapped
  • Applies physics to the player e.g. gravity, jumping and movement forces

Screen Definition and Loading

The "entire game" (all two screens of it, at this stage) takes place in the one XHTML page Default.htm. Each game screen is defined in its own JavaScript file, named by its X and Y coordinates, e.g. screen_2_1.js. When the player enters a new screen, the following occurs:

  • The game loop is suspended
  • All child Elements of the Viewport excluding the player are removed
  • The Viewport's child Element array is re-initialized
  • The current screen's JavaScript file is dynamically removed from the document head
  • The new screen's JavaScript file is dynamically appended to the document head
  • The new screen script adds child Elements to the Viewport via convenience methods in the Viewport class, e.g. addElevator
  • The new screen script re-commences the game loop

This approach means that technically as many new screens as desired can be added without altering the main engine, or (I would hope) increasing the amount of memory utilized. I admit I have not done in-depth profiling of browser memory as JavaScript files are linked and unlinked and DOM elements added and removed. I presume it depends upon how garbage collection is implemented (or not). I would be interested to hear from anyone with expertise in this area.

W3C Standards and Browser Behaviour

I'm quite pleased to have been able to get this game to validate as XHTML (1.0) Strict, with all JavaScript unobtrusively linked. It is thus far tested and known to work in Firefox, Opera, and Internet Explorer 7 on PC. I did have it basically working in Internet Explorer 6 (after a few CSS hacks, which pained me greatly) but then gave up on the effort and have not yet been able to preview the latest version in Internet Explorer 6. It is not a commercial "site" and the intention was to upskill on standards-based approaches rather than to absolutely maximize compatibility. Therefore I'm happy with my decision not to introduce the clutter of conditional browser comments or any further CSS hacks.

One point of note regarding browser performance is that while Opera, in my experience, is generally the fastest browser of the three mentioned above, when it comes to JavaScript-manipulated DOM elements, it seems to be the slowest by a large margin. It appears to slow down dramatically as the number of moving sprites increases. I have just upgraded to version 9.25, which seems to be faster than the previous version, but it still struggles compared to Firefox and Internet Explorer 7.

Conclusion

While my field of work does not involve any kind of game development, the extra understanding I've gained through this project has already served me well in our commercial undertakings, most of which directly or indirectly leverage JavaScript to varying degrees. Hopefully, some of the approaches might be useful to others. I make no claims that this code is perfect, as large parts of it are fairly new to me! Comments and helpful criticism are much appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)