Article
The Art and Science of JavaScript
The resetDungeonView Method
The resetDungeonView method applies style properties (size, position, clip, background, and border-color) to the elements that form the most basic view -- that shown when our user is looking straight down a corridor that stretches the maximum distance that our script can support, as depicted in the figure below.
![]()
This method can be called whenever we need to reset the view, which we'll do at initialization, and again before applying each new view. It works by iterating through the matrix of element references we created in createDungeonView; it calculates the width of each column and the height of each of the bricks inside it.
To perform this calculation, we need to define some structural constants. These constants can be found in the configuration script, config.js, which is also in the code archive's scripts directory:
Example 6.3. config.js (excerpt)
this.viewsize = [600, 400]; // [1]
this.gridsize = [16, 4]; // [2]
this.bricksize = [50, 31]; // [3]
this.multiplier = 0.84; // [4]
These constants represent the following values:
- The viewsize represents the total width and height of the view container.
- The gridsize represents the number of columns from the edge of the viewsize to the center, and the number of bricks from top to bottom.
- The bricksize is the size of the upper and lower (triangle-creating) bricks.
- Finally, the multiplier controls the factor by which the brick size is reduced for each column as we move towards the center of the view.
The figure below shows the same perspective diagram that we saw in the previous figure, this time with captions indicating how each of these structural constants applies.
Working Out the Values
I'd love to say I had a clever mathematical algorithm for calculating the values I've used here (and there probably is one), but I can't. I just used trial and error until I arrived at something that looked about right. Note, however, that the values are very closely interrelated, so be extremely careful when adjusting them!
The choice of correct values is also dependent upon the overall performance of the script -- it would be possible to create a higher resolution maze with a larger number of smaller bricks. However, that would mean we had more objects to render, which would result in lower overall performance. Even with the default values that I've set above, you need a fairly decent computer to render this maze effectively.
![]()
If you have a look at the above figure, you'll notice that the bricks line up perfectly -- in each column, the upper brick is exactly below and to the side of the upper brick in the previous column; likewise, each lower brick lines up below and to the side of its neighbor. The clip and position values of the inner elements of those bricks decrease proportionally as the brick size decreases, while the height of the top and middle bricks changes as necessary to complete the wall.
Finally, in order to improve the appearance of perspective, we want each column to be slightly darker than the previous one. To achieve that goal, I've introduced constants that define the base color of our bricks and the darkening proportion that's applied to them. We'll define the wallcolor using RGB values -- they're easier to work with, as the values are decimal rather than hexadecimal. We'll name the constant that controls the darkness of each column the darkener. Both of these constants are defined in the config.js file:
this.wallcolor = [127, 0, 0];
this.darkener = 0.95;
On each iteration of our code, we render a single column on each side, moving towards the center of the view; the base color is darkened by the amount specified in the darkener constant. I chose a dark red for the main demo (dark colors generally work best), but as the next figure shows, you can use any color you like -- even pink!
![]()
The applyDungeonView Method
The applyDungeonView method applies style variations to the basic view, creating passageways off to either side of our main passage. To do this, it first compiles a matrix, stored in the variable this.squares, which is a subset of the complete floor plan. This matrix consists of only those floor plan squares that are necessary for us to render the player's view from the current location in the maze.
The figure below shows an excerpt of a floor plan. The green square highlights the spot where the player is currently standing, while the blue border surrounds what the player can see. It's the region inside this blue border that defines the part of the plan required to draw the view for the player.
In this example we're looking north, and each of the floor squares provides us with information about the surrounding squares. However, for any direction of movement, the player is always looking "forwards," and it's the player's view that we render. So the first thing we must do is translate the data contained within each square into data that's accurate for the direction in which the player is facing. Let me explain this with an example ...

Remember that the digits in a square indicate the presence of wall or floor surrounding that square, in clockwise order, starting from the top. Well, we want those four digits always to indicate that information clockwise from the top, regardless of the direction in which the player is actually facing. Should we have the value 1110 when facing north, then, when the player was facing east, that same square would be represented by the value 1101. When the player faced south, the value would be 1011, as shown here.
![]()
So, as we compile the this.squares matrix, we need to translate each square's value to the direction in which the player is facing. A small utility method named shiftCharacters performs this translation: str is the four-digit string, and shift is the number of times the square must be rotated in a counterclockwise manner when the player turns in a clockwise direction. Each turn corresponds to each of the four digits that represent that square moving to the left by one position (with the left-most digit jumping to the end of the string).