Article
The Art and Science of JavaScript
To continue with the example in the figure above, if the player's view was to change from north (with floor plan data of 1110) to west (0111), the shift value would be 3.
The shiftCharacters method looks like this:
Example 6.4. underground.js (excerpt)
DungeonView.prototype.shiftCharacters = function(str, shift)
{
var saved = str.substr(0, shift);
str = str.substring(shift);
str += saved;
return str;
};
Once we have the data we need, we can iterate through it and create the actual view. This is where things get rather tricky.
First of all, we need to iterate forwards through the squares, starting from the player's current location. With each iteration, we test the first digit of each square (which tells us what's in front of it) until we find the end wall. The end wall marks the limit of what the player can see -- every column from that point onwards should be assigned the same height and color. These columns will create the illusion of a facing wall, as shown in the figure below.

Once we know the limit of the player's view, we iterate from that point backwards through the floor plan data towards the player's location, looking for adjoining passageways. We need to iterate backwards because the height of a passageway's facing wall is the height of the furthest column that defines it.
To illustrate, the figure below shows another excerpt from the perspective diagram, this time with lines and shading overlaid to show a corridor with a passageway off to the left.

If we want those second and third columns to create that passage to the left, we need to remove the upper and lower bricks from those columns, leaving only the middle bricks, which then must be resized as necessary. But our passage is two columns across, and it's the furthest column (or what we might call the corner column) that determines the height of the wall -- not the nearest. So we need to modify that corner column first, so that we know how tall to make the adjacent columns.
Iterating forwards would require us to jump two steps ahead to find the corner, then move one square back to make a further adjustment. And that's why we iterate backwards, rather than forwards. (I told you it was tricky!)
When we create those passageways, we also lighten the facing walls slightly, to improve the visual appearance and make the wall look more realistic. As we did when we darkened the walls, we use a single constant value (I've called it the lightener) to determine the amount of lightening required:
this.lightener = 1.25;
As with the height value, the lightening is applied to the corner column first, then copied onto the nearer column (for the same reasons). And once again, as with all of the constants used in this script, I have no magic formula to share for how these values were obtained?they're just what looked right after trial and error.
The figure below shows the same view excerpt again -- this time without the exposed construction -- looking as it does in the final game.

Applying the Finishing Touches
Now, I hope, you should have a fairly concrete sense of how the script generates perspective views, with walls and passages created as necessary. From the diagrams we've seen so far, you can understand that any given view is simply a combination of rectangles and triangles.
One final touch we'll need to make is to shift the entire view up inside the container in order to raise the horizon slightly. This is just another visual tweak that I included because I think it produces a better-looking and more realistic result, as the figure below shows.
![]()
You'll notice I've used images for the sky and floor patterns. These images provide some texture to add to the realism of my maze; they also contain a slight gradient, growing darker as they approach the horizon, which again reinforces the sense of perspective.
The end result is not perfect, though: unavoidable rounding errors occur in the final output figures, and these errors give rise to an occasional discrepancy of one or two pixels between adjacent columns. The shading computation is not exact either -- sometimes, on close walls, you can see a slight color difference between two columns that should be exactly the same.
All things considered, however, what we've created here is a reasonably convincing 3D maze.