Click here to Skip to main content
Email Password   helpLost your password?

MiniRacer Screenshot

Introduction

The code behind this short JavaScript game is aimed at the intermediate level and is intended to demonstrate the useful techniques of:

As such, this game is aimed at Internet Explorer 6 (& compatible) browsers. For simplicity (and an easy life), I have not stepped into the muddy waters of browser compatibility, but one big reason for supporting the W3C DOM is the hope that in time code across browsers will become increasingly standardized.

This article starts with an introduction to the game and the general principles of inheritance (prototyping in JavaScript). For the specific details regarding extending HTMLElements, take a jump down towards the end of this article.

I should make clear that whilst, hopefully, this game may be a good example of a few useful techniques, they are not techniques of my own invention. Some are well known; others less so. The References section at the bottom of this article directs the reader's attention to the four sources which were the most useful to me in writing this game.

The Game

The game is a simple driving game that I first met on my Amstrad CPC464, quite a few years ago. It utilizes a table to represent the race-track, with table cells making up the squares of the race-track. Four buttons allow you to steer the car in the four possible directions. The aim is to drive to the finish (indicated in red) without crashing into the walls.

You can also design a new track to race on, change the design of the current track, and even save a designed track by adding the URL to your Favorites (the track is stored in the querystring).

You can play the game online, or download the ZIP file which contains all of the code.

The Design

It would be possible, of course, to program games like this using a number of global functions that test various conditions and write HTML to the document using document.write and modifications to the innerHTML property of elements.

However, the benefits of a more object-oriented approach are substantial. In particular:

Because JavaScript does not force any kind of OO structure upon your code and, in addition, is a loosely typed language, an application of sheer discipline on your part is required to get started on this technique.

The basic principles shown below need some careful study to master, but, once mastered, offer big rewards. Although it may look complicated at first, it is also easy to step back and admire the basic simplicity of an object-oriented solution. The UML diagram for this solution is shown below.

UML Diagram

UML Diagram

The advantages of 'inheriting' from W3C DOM elements are two-fold:

Design Principles

We can implement this approach using the following general principles:

  1. As in any object-oriented approach, decide on the classes needed for your program. On this occasion, I've used:
  2. Create a js file for each of your classes, e.g., RaceTrack.js. Each class js file is set out in the following fashion:
  3. Create another js file for each of your web-pages to contain the static code for that page. For instance, in this project, I have two web-pages: miniRacer.htm and designTrack.htm, so I create two matching js files: miniRacer.js and designTrack.js which contain the static functions for the page.

    As an example, miniRacer.js contains the following global functions and variables:

    var tbl;    // stores the race track table
    
    var car;    // stores as instance of the Car class
    
    
    // The default track to race on
    
    var DefaultRaceTrack = "wwwwwwwwwwwwrrr...";
    var DefaultRowLength = 11;
    
    function setup()
    {
      // Load racetrack in from querystring,
    
      // using default track if not available
    
      var qs = new QuerystringParser();
      var track = qs.getRaceTrack(DefaultRaceTrack);
      var rowLength = qs.getRowLength(DefaultRowLength);
    
      tbl = document.createElement("RaceTrack");
      tbl.load(track, rowLength, DISABLE_CELL_CLICK_HANDLER);
    
      // add racetrack to webpage
    
      var RaceTrackContainer = document.getElementById("RaceTrackContainer");
      RaceTrackContainer.replaceChild(tbl, RaceTrackContainer.firstChild);
    
      // create a new instance of our racecar
    
      car = new Car(tbl, "car", new SpeedSelector("speedSelect"));
    }
    
    function designTrack()
    {
      // called when "Design This Track" link
    
      // is clicked...redirects to designTrack.htm
    
      car.reset();
      window.location = "designTrack.htm?track=" + 
         tbl.serialise() + "&rowLength=" + tbl.getRowLength();
    }

    Notice how little code there is in the global functions. In an ideal situation, these do little more than creating the needed instances for the classes and then hooking them up as needed. Essentially, all of the code has been delegated into the most appropriate method of the most appropriate class. Each method can then be fairly easily unit tested when things go wrong, making for much easier testing and debugging.

  4. Create a constants.js file to store code constants. E.g.:
    var ENABLE_CELL_CLICK_HANDLER = true;
    var DISABLE_CELL_CLICK_HANDLER = false;
    var IGNORE_QUERYSTRING = true;

    [Alternatively, you can attach the constants to their closest connected functions but I haven't gone that far.]

  5. Finally then, we need to create DOM_override.js file to implement the override needed in the document.createElement method provided by the W3C DOM. The need for this and an explanation is given below.

Inheriting from DOM Elements

Here is the small catch. Usually, to make use of JavaScript's function-based equivalent to inheritance, we simply set the prototype object of a function to be the function that we wish to inherit from:

Dog.prototype = new Animal();

However, in Internet Explorer, DOM elements are not implemented using full JavaScript functions. This leads to error messages when using the prototype technique.

All is not lost though. Instead, we do two things in DOM_override.js:

It simply makes use of the fact that all properties and methods of a JavaScript function are available through the index notation [].

We iterate through all the properties and methods of our class, copying them to the instance of the HTMLElement. We then return the instance of the HTMLElement which is now equipped with all the properties and methods of our class. This achieves the inheritance, albeit in a rather unorthodox and manual way.

We can now create instances of our classes. We create an instance of our top level class RaceTrack using, e.g.:

var tbl = document.createElement("RaceTrack");

To enable the creation of instances of RaceRow and RaceCell, I add factory methods to RaceTrack and RaceRow respectively:

function RaceTrack_insertRaceRow()
{
  return document.applyInherit(this.insertRow(), new RaceRow());
}

function RaceRow_insertRaceCell()
{
  return document.applyInherit(this.insertCell(), new RaceCell());
}

this.insertRow and this.insertCell are defined for us within HTMLTableElement and HTMLRowElement respectively.

These neat functions now enable us to add the RaceCells to the track, using the following example code:

for(var i=0;i<rows;i++)
{
  var row = this.insertRaceRow();
  for(var j=0; j<cols;j++)
  {
    var cell = row.insertRaceCell();
    cell.setX(j);
    cell.setY(i);
  }
}

The instance of RaceTrack (referenced below by tbl) can be plugged into the web-page using:

var RaceTrackContainer = document.getElementById("RaceTrackContainer");
RaceTrackContainer.replaceChild(tbl, RaceTrackContainer.firstChild);

For this to work, we need an element in the web-page with an id of "RaceTrackContainer".

<td id="RaceTrackContainer">
  Racetrack goes here
</td>

References

I hope this article has been of some help to you. Some references are given below, all of which have been helpful in constructing this article.

  1. Emulating Prototyping of DOM Objects in Internet Explorer.

    This web-page covers extending the DOM elements with IE and JavaScript. It also shows a neat trick for adding functionality using Behaviors. The technique shown here is basically the idea used in this game.

  2. JavaScript Objects [Nakhimovsky & Myers, Wrox Press, ISBN 1861001894]

    A clever book covering an OOP approach to JavaScript. I've used some of the ideas from this book in this game.

  3. Access the Querystring client-side.

    Using a QueryString object to parse the QueryString. My simple QuerystringParser class comes basically from here with some negligible modifications.

  4. JavaScript: The Definitive Guide [David Flanagon, ISBN 0-596-00048-0, 4th Edition]

    An excellent, general JavaScript Reference. This book also includes a useful reference to the W3C DOM.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionI'm not sure how to even get permission to upload it here.
tpgames
10:37 9 Feb '07  
Can Paul Baker, or someone who is connected with this site download the zip file and host it on the appropriate page so that Firefox uses can get it?
Thanks! I'm very new here, and do not know how to go about doing this.

When and IF I see the zip file here for Firefox, then I'll take it down off of my site. (It is conceivable, that I will forget to even check. Laugh )

tpgames jaunting off to finally finish her website...and I have another one for you!

AnswerRe: I'm not sure how to even get permission to upload it here.
Paul Baker
1:30 10 Feb '07  
Hi

Thanks for your comments and your helpful link to the firefox compatible zip.

I've never tried to update an article before but will give it a go in a day or two and see if I can incorporate the additional zip file!

Kind regards


Paul Baker
GeneralRe: I'm not sure how to even get permission to upload it here.
tpgames
20:22 10 Feb '07  
If you can, great. If you can't, then I will keep the link on my site.Big Grin

tpgames jaunting off to finally finish her website...and I have another one for you!

NewsConcerning zip of game for Firefox users
tpgames
10:30 9 Feb '07  
If anyone is interested in a zip file
premade for firefox only, it can be gotten here:
ffminiracer.zip

It contains everything the original zip file has, except has the necessary changes in the files to make it compatible with FireFox.
These files were copied directly from the site he gave a link to that has the firefox version of the game.
No adware, etc. should be found.

The above link will be valid until I can get them to host it here. Thanks!



tpgames jaunting off to finally finish her website...and I have another one for you!

GeneralDoesn't work with window.Option
pellyg
9:27 9 May '06  
Nice job on the game.
I'm tring to do the same thing except using the JS function for creating <option>s inside a select box. I have code that assigns an ".action" to each option below. However, when I try to clobber window.Option below, I get an error in IE ("Error: Not Implemented").   Firefox works fine. Any ideas why?

var __oldOption = window.Option;

//this part works. I can create OnclickOption()s and they work.
OnclickOption = function(n,v,clickAction) {
var opt = new __oldOption(n,v);
opt.action = clickAction;
return opt;
}

window.Option = OnclickOption; //throws an error in IE, works in FF

Thanks,
Greg
GeneralDoes not work with Firefox!
Arno Nym
10:45 19 Feb '05  
Dead
GeneralRe: Does not work with Firefox!
Paul Baker
11:05 20 Feb '05  
Ok you're right of course. I did say at the top of the article that I wasn't really interested in muddying this example with cross-browser complications but hey, here are the changes you'd need to make to get it to work on FireFox:

1) Change DOM_override.js to:

function applyInherit(original, inter)
{
for (method in inter)
original[method] = inter[method];
return original;
}

2) Find 2 references to document.createElement("RaceTrack") in miniRacer.js and designTrack.js and change to the less elegant:

applyInherit(document.createElement("table"), new RaceTrack());

3) Find 3 references to document.applyInherit and remove 'document.' to leave simply as applyInherit(...); [in RaceTrack.js, RaceCell.js and RaceRow.js]

4) Find this.insertRow(); in RaceTrack.js and change to this.insertRow(-1);
Do the same with insertCell in RaceRow.js

Now it will work on FireFox latest version. Shame that the ability to redefine document.createElement has to go (and adding extra function applyInherit also changes to global function) but there we are.

I've changed the online version (http://eh0627.empetushosting.net/OOP_MiniRacer/miniRacer.htm[^]) to be FireFox friendly!

Hope this helps.
GeneralRe: Does not work with Firefox!
Cd-MaN
22:39 27 Feb '05  
Ok, it works now. Nice little game.


Last Updated 16 Feb 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010