The illusion of 3 dimensions

I decided that I should write a post about some of the basic mechanics behind a DHTML isometric world.

The browser environment these days, with CSS and javascript interaction with the DOM (Document Object Model), gives rise to something more akin to a web-based software application, than a static multimedia document.

The following explains my method to constructing the isometric grid space.

The Tile Grid:

The world is built up using png transparent “tile” graphics which contain a diamond shape of a set ratio. 2:1 width:height.

An enlarged view of a single tile

An enlarged view of a single tile

The tiles are rendered into the DOM using javascript from the very top of the diamond, at coordinate x1,y1. Progressing first along the X axis for Y1 and then repeated for Y2, etc.

Each tile has a CSS zIndex higher than the next, and each tile is positioned absolutely.

A formula is used to determine the top and left position of each tile. This requires some initial variables.

gridSize: the number of tiles on each axis of the grid

tileH=20: the width of the diamond contained within each tile image

tileW=40: the height of the diamond contained within each tile image

tileNum: starts at 1 — x2,y1=tileNum(2) | x3,y1=tileNum(3) and so on..

tileLower: the Zindex floor of the tileSet. (it is important to plan out the Zindexes in an application such as this)

The javascript code in it’s most basic form:

var gridSize=20;
var tileH=20;
var tileW=40;
var tileNum=1;
var leftStart;
var topStart;
var lastTop;
var lastLeft;
var thisTop;
var thisLeft;
var zInd;
var tilePath;
for (var y=1;y<=gridSize;y++){
	leftStart=((gridWidth/2)-(tileW/2)-((y-1)*(tileW/2)));
	topStart=((y-1)*(tileH/2));
	lastTop=topStart;
	lastLeft=leftStart;
	for (var x=1;x<=gridSize;x++){
		thisTop	= lastTop + (tileH/2);
		thisLeft= lastLeft + (tileW/2);
		zInd=tileLower+tileNum;
		tileNum++;
		tilePath="tiles/tile1.gif";
		placeSprite(thisTop,thisLeft,tilePath,zInd);
		lastTop=thisTop;
		lastLeft=thisLeft;
	}
}

This code shows the most simple implementation of this concept.

You may already see the potential. the tilePath value used to place the sprite could be modified using the x and y values to read from an associative array containing data from a database.

In my implementation, the placeSprite function takes some extra parameters including a reference id, (x+”_”+y), and a setID (e.g. “tileUnderlay”) which is used to create an array of sprite img objects. They can be modified at a later date by calling on the array. E.g.

spriteArray["tileUnderlay"]["1_15"].src="emptyTile.png";

The result of the simple application detailed above would look like this.

The result of the javascript grid construction

The result of the javascript grid construction

Extra layers:

In my implementation of the isometric grid space, I also envisioned objects and terrains overlaying the tiles set out earlier.

These layers share an almost identical functionality, but occupy a different zIndex layer, allowing for objects to be placed over the top of terrains.

To create the illusion of 3 dimensions, the Zindex is very important.

An object or terrain item being read from the database will have a tilesWide and tilesHigh attribute, allowing us to determine the maximum X and Y values, and thus the foremost position in our point of perspective.

Below is my code for placing objects into the grid space. Take particular note of the zIndex construction.

The value tileNum is important. Also take note of the way I derive this value.

objectArray contains information about the placement of the object at tileX,tileY.

objectProperties contains information relating to particular objects, referenced by their objectID.

objectOverhang is the pixels above the top corner of the tile floor that the object image extends.

E.g. an object 1 tile wide and 1 tile high, where each tile is 20pixels high and 40 pixels wide. If the object image height is 50 pixels, the objectOverhang would be 30pixels. The image would be rendered in the DOM at 30 pixels higher than the tile’s top value.

"renderObject":function(tileX,tileY){
	var objectID;
	var objectX;
	var objectY;
	var objectZ;
	var thisTop;
	var thisLeft;
	var tilesHigh;
	var tilesWide;
	var maxX;
	var maxY;
	var zInd;
	var tileNum;
	var objectOverhang;
	var objID;
	objectID=objectArray[tileX+"_"+tileY]["objectID"];
	objectX=objectArray[tileX+"_"+tileY]["tileX"];
	objectY=objectArray[tileX+"_"+tileY]["tileY"];
	objectZ=objectArray[tileX+"_"+tileY]["objectZ"];
	tilesWide=objectProperties[objectID+""]["tilesWide"];
	tilesHigh=objectProperties[objectID+""]["tilesHigh"];
	objectOverhang=objectProperties[objectID+""]["objectOverhang"];
	objID=objectX+"_"+objectY;
	maxY=parseInt(objectY)+parseInt(tilesHigh)-1;
	maxX=parseInt(objectX)+parseInt(tilesWide)-1;
	tileNum=((maxY-1)*gridSize)+maxX;
	zInd=objectsLower+tileNum;
	thisTop=((objectY-1)*(tileH/2))+(objectX*(tileH/2))-objectOverhang-objectZ;
	thisLeft=(gridWidth/2)-((objectY-1)*(tileW/2))-(tilesHigh*(tileW/2))+(objectX*(tileW/2));
	tilePath="objects/"+objectID+".png";
	placeSprite("objects",objID,thisTop,thisLeft,tilePath,zInd);
},

Page 1 of 2 | Next page