Export (0) Print
Expand All

Full Circle

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.

Michael Wallent
Microsoft Corporation

June 5, 2000

Contents

Rock-o-Genesis
Making Rocks Fly
Other Things

I apologize for skipping last month's column—my wife unexpectedly had our new son early last month, and, as you might guess, things have been pretty crazy. Anyway, on to this month's column.

In 1996, when I first started what was going to become the DHTML Dude column, I wrote a DHTML version of the arcade classic, Asteroids. It was one of the first cool DHTML applications that showed how Microsoft® Internet Explorer 4.0 was drastically different from the HTML 3.2 Web. So, while I was rooting around in my hard drive the other day, I saw that first version of Asteroids and fired it up in Internet Explorer 5.5—and it worked! (Good thing that we have compatibility from version to version.)

I then went back through the code and made a set of changes that improved both the functionality and performance of Asteroids. Some changes were based on new functionality we added for Internet Explorer 5, while others were just changes that came from new understanding of the system behavior. Let's take a look at the changes and break up some rocks!

Rock-o-Genesis

View the Asteroids sample.

One of the hardest problems I faced in the original version was managing the creation and destruction of rocks. In Internet Explorer 4.0, the only way to create new content on the page was by using innerHTML or insertAdjacentHTML(). My early tests of these methods were too slow. Instead, I hacked together a method by creating an array of 14 rocks. Each of these rocks was given a special ID that indicated its size. If I wanted to hide or show a rock, I would toggle visibility on the appropriate rock. This method had major limitations that prevented me from having more than 14 rocks on screen at a time. It also caused a performance problem—I had to loop through the entire array with every tick in order to animate the rocks across the page.

In version 5, we introduced an easier way to create new elements: the W3C DOM cloneNode() method. This method makes it much easier to create new content, as well as solve the performance issues, by allowing more than 14 rocks on the page. As a result, after you clear a screen of rocks in round one, you get more and faster rocks in round two. Instead of having to iterate through an array, I create all of the rocks in a known container, "RockContainer," and iterate over the children collection of this element, knowing that it only contains images that are rocks.

Here's a comparison between the old code to create a rock and the new code. First, the old code:

function splitRock(aRock) {
  var newR1, newR2, f, f2;
 
  f = aRock.factor;
  f2 = f + 1;
  if (f == 3) {
    aRock.src = aRock.srcBase +"1.gif";
    aRock.className = "rocke";
    aRock.animcount = 1;
    aRock.style.color = "#000001";
 
    aRock.dx = 0;
    aRock.dy = 0;
  }
  else {
    newR1 = document.all(aRock.id + "a");
    newR2 = document.all(aRock.id + "b");
 
    aRock.src = aRock.srcBase +"1.gif";
    aRock.className = "rocke";
    aRock.animcount = 1;
    aRock.style.color = "#000001";
 
    aRock.dx = 0;
    aRock.dy = 0;
 
    newR1.style.posTop = aRock.style.posTop;
    newR1.style.posLeft = aRock.style.posLeft;
    newR2.style.posTop = aRock.style.posTop;
    newR2.style.posLeft = aRock.style.posLeft;
 
    initializeRock(newR1, f2);
    initializeRock(newR2, f2);
  }
  soundExplode();
}

This is the method that's called when a rock is going to explode. I need to check to see if it's a small rock; that's the f=3 check at the top. If so, then instead of splitting, I show a three-frame explosion animation. If the rock needs to be split, I have to find the right rocks by ID and initialize them. As you can see, the old code is very clunky and doesn't scale well. Here's the new code:

function splitRock(aRock) {
  var newR1, newR2, f, f2;
 
  f = aRock.factor;
  f2 = f+1;

  if (aRock.nextRock == "NONE") {
    aRock.src = aRock.srcBase + "1.gif";
    aRock.className = "rocke";
    aRock.animcount = 1;
 
    aRock.dx = 0;
    aRock.dy = 0;
  }
  else {
    newR1 = createRock(theDoc.all(aRock.nextRock));
    newR2 = createRock(theDoc.all(aRock.nextRock));
 
    aRock.src = aRock.srcBase+"1.gif";
    aRock.className = "rocke";
    aRock.animcount = 1;
    aRock.dx = 0;
    aRock.dy = 0;
 
    newR1.style.posTop = aRock.style.posTop;
    newR1.style.posLeft = aRock.style.posLeft;
    newR2.style.posTop = aRock.style.posTop;
    newR2.style.posLeft = aRock.style.posLeft;
 
    initializeRock(newR1, f2);
    initializeRock(newR2, f2);
  }
  soundExplode();
}
 
function createRock(rockTemplate) {
  var r;

  r = rockTemplate.cloneNode(false);
  r.id = "ActiveRock";
  eRockContainer.insertBefore(r, null);

  return r;
}
 

Now, each rock has a new property, nextRock. This is the ID of the rock template that should be cloned to create the appropriate children of the rock. Instead of putting 14 images on the page to begin with, I put three on the page—a "template" for each of the rocks that might be created. I use this method to create dynamic content all the time. It's a good tradeoff between declarative content and procedural content. Here's the HTML for the templates:

<IMG CLASS=rock ID=TR1 nextRock=TR2 SRC="rock1.gif" orgSrc="rock1.gif" srcBase="rock1e" POINTS=20
STYLE="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 10">
 
<IMG CLASS=rock ID=TR2 nextRock=TR3 SRC="rock2.gif" orgSrc="rock2.gif" srcBase="rock2e" POINTS=50
STYLE="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 11">
 
<IMG CLASS=rock ID=TR3 nextRock=NONE SRC="rock3.gif" orgSrc="rock3.gif" srcBase="rock3e" POINTS=100
STYLE="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 13">

To create the rock, I call the cloneNode() method, then insert the new rock into the RockContainer object. One note on this mechanism: Make sure that you reset the ID each time, or you will have two elements with the same ID, causing the second clone call to fail.

Now, when a rock is finally deleted, instead of setting the visibility back to hidden, I use the removeNode() method to remove it from the tree.

Making Rocks Fly

After three years of using a development platform like Internet Explorer, you start to learn the tips and tricks to make your code faster. I've written about performance in a relatively abstract and simple way before, but with a large application like this, it's easy to see the performance techniques in context. Also, because this application is animation focused, performance each time through the loop is critical. One slow thing will gum up the whole works.

I did two major things to improve performance: code rework and variable caching. For code rework, there's really no magic; I just took a fresh look at the code to try to reduce the steps. Making a DHTML page faster is the same as making a Win32 page faster: Write smaller and smarter code (no secrets there). The big DHTML improvement was to change all of the common references to the document, as well as collections and elements to global variables from direct access to the elements. You should do this because every time you refer to "document," code in the script engine uses COM IDispatch to talk to MSHTML to figure out what that name means. Many millions of processor cycles later, it finds the instance and returns it. However, each time you refer to a variable name, it stays completely in the context of the script engine, and many fewer calls are made.

I cached these things:

// performance items
var theDoc = document;
var eLaser1 = Laser1;
var eLaser2 = Laser2;
var eLaser3 = Laser3;
var eRockContainer = RockContainer;
var eRockChildren = eRockContainer.children;
 

These cached items were the ones I accessed most in key methods like moveRocks(). Caching made the animation feel much smoother. It's hard to tell exactly what this gained, but it was enough to make the animation of 30-plus rocks function smoothly.

Other Things

There are many more changes that could be made here to really take advantage of Internet Explorer's new capabilities. For example, instead of using images with expando properties, why not make them DHTML behaviors for more encapsulation? Or why use images at all? You could instead use VML PolyLines to create real vector graphic ships and rocks, that way the ship wouldn't require 16 images to turn full circle.

It's cool to remember how full the platform was in the version 4.0 days, but it's even cooler to see how Internet Explorer has grown to be an even more powerful platform for creating useful things, not just Asteroids.

 

DHTML Dude

Michael Wallent is Microsoft's product unit manager for Internet Explorer.


  
Show:
© 2014 Microsoft