Isometric tiles are just as easy to achieve as a standard tile map. With a bit of shape alteration and minor positioning adjustment, you can change your flat 2D game concept into a mind blowing “2D.5″ isometric engine, capable of graphical destruction and full user awe.
But where do you begin in creating such an experience?
To the drawing board!
So you start off with a 50×50 pixel square which represents part of the fields of Felderon, which gives your hero enlightenment while walked on, at the risk of a slight game ending seizure. Such a tile may look like this:
However this hero isn’t walking on any standard field of Felderon, he is standing on an isometric field of Felderon. So how do we change our tile to meet these requirements?
Basically we rotate it and half the height so our tile is now 50px wide and 25px tall. Instantly our field tile looks like it has some slight depth to it with the grass blades of possible healing growing out. pngs are used for the alpha and to allow image overlap. This tile when placed into our soon to be coded engine would look like this:
So we have our tile and now we need to alter our previous example on creating a tile engine to match the isometric view.
First however we will change the code to load in this image rather than drawing a tile.
A very simple way to do this is to create an array of images. Although our Felderon field tile is of a graphically inspiring status, we will need some other tiles to go along with it. Feel free to use this tile which represents the sea of despair, that when drank generously offers -8 Charisma with a 30% chance of growing an 11th finger. Not to be confused with the river of despair that we shall not go into.
var map = Array([1,0,0,0],[1,0,0,1],[0,0,1,1],[1,1,1,1]);
var tileDict = Array("water.png","land.png");
var tileImg = new Array();
var loaded = 0;
var loadTimer;
So in this simple method for loading multiple tiles, tileDict will hold all our tile images for loading and tileImages array will contain the loaded images for accessing.
Loading the images in can be achieved with the following loop:
function loadImg(){
for(var i=0;i<tileDict.length;i++){
tileImg[i] = new Image();
tileImg[i].src = tileDict[i];
tileImg[i].onload = function(){
loaded++;
}
}
}
Pretty simple code really, runs through the length of your tileDict and as each image is successfully loaded it increments loaded by 1.
If you create an initialize function which contains your ctx setup, then using the following code we can call the loadImg function as well as creating a timer used for checking if all tiles have been loaded.
function init(){
ctx = document.getElementById('main').getContext('2d');
loadImg();
loadTimer = setInterval(loadAll,100);
}
Our loadAll function is setup as the following:
function loadAll(){
if(loaded == tileDict.length){
clearInterval(loadTimer);
drawMap();
}
}
Again pretty simple. Every 100ms loadAll is called and we check if the loaded counter is equal to the tileDict length. If both are equal we cancel the canvas loadTimer using clearInterval and call the drawMap function.
function drawMap(){
var tileH = 25;
var tileW = 50;
// Set as your tile pixel sizes, alter if you are using larger tiles.
var mapX = 50;
var mapY = 50;
// mapX and mapY are offsets to make sure we can position the map as we want.
for(i=0;i<map.length;i++){
for(j=0;j<map[i].length;j++){
var drawTile= map[i][j];
var xpos = (i-j)*tileH + mapX;
var ypos = (i+j)*tileH/2+ mapY;
ctx.drawImage(tileImg[drawTile],xpos,ypos);
}
}
}
Exactly the same as the last tile post we loop through the array lengths however the tile positioning code is slightly different.
Taking in the changes in size and the fact that we are now positioning for an isometric view we calculate the position accordingly.
drawTile is set as the value of the current map array tile, and used for getting the matching tile image from the tileImg array.
And behold the fields of Felderon with the sea of despair flowing powerfully through it. That really is all there is to it. As you can see in the screenshot there is slight lines between each tile. This could be fixed by setting tiles to have an Anti-Alias edge rather than hard, or by just increasing the tiles width by a couple of pixels.
For the full source code of the above see here:
http://glacialflame.com/tutorials/tiles/01/
Our next tile post will cover selecting isometric tiles with the mouse and changing tiles via mouse clicks.







I think your introductory tutorials are great and I do hope you keep the flame alive and keep working on follow up tutorials as promised.
Looking forward to tutorials on mouse interaction and possibly even moving around the map?
Thanks again!
I’m 99% sure Iain is planning on doing a mouse / movement / keyboard tutorial, I’ll mention it anyway
More posts, tutorials and the video will be coming hopefully very soon.
Thanks for the response. Could I ask you quickly.. your tutorial for the tilemaps doesn’t explain how you implement things like the Candle Stick and other objects that obviously have a height higher than the tile size of 50×50. You could be doing this a number of ways I guess.. namely by building individual tile graphics for every element (not a great idea), having tiles that are actually higher than they are wide and using an offset value when placing them on the canvas or you could be layering down objects as a separate layer?
I’d really love to know in simple terms which method you are using to lay down your candle sticks
Hi Chris,
Much thanks for the feedback, been on holiday for a week so sorry for not getting back to you sooner. In GF we are currently using like you guessed 2 layers basically, an object layer and a tile layer. Tiles are drawn out first then another object array is ran through and drawn on top of the tiles.
I’ll aim to have a tutorial on the second layer and mouse control in the next couple of days up (basically enough to cover a simple ISO editor).
All the best!
Such great news. I’m trearing my hair out with figuring out mouse interaction. Was about to give up and move to SVG instead of Canvas. But I will hang fire and very much look forward to your upcoming tutorials!
Excellent post thank you!
Sent via Blackberry
I found your site on Google searching how to implement isometric tiles for my Android game project. Works great; big thanks.
Do you have any comments on the Aves Engine? Specifically that they found better performance in NOT using the canvas element?
http://paulbakaus.com/2010/07/19/why-canvas-is-not-an-obvious-choice-for-web-games/
Hi Rich, the Aves engine is looking pretty impressive and definitely a lot further on than where Craig and I are. Some of the features they’ve already got in are rather neat and we have only briefly touched on some of the aspects.
At this stage I can’t really comment, I could only take his word for most topics but that isn’t much fun.
In GF we are playing with areas built of about 10k tiles with only about 80 tiles and possibly 80 objects being rendered on screen at a time, alongside the particle engine which has its effects attached to tiles. Both the latest FF and Chrome meet our aim of 26FPS no bother. In Chrome it stays a solid 26FPS when moving around and firing projectiles and so on quite happily.
It does however lag badly on the Iphone 3GS, but right now we are only aiming for desktop browsers. I think I’ve seen a video of their engine running quite happily on the iPad which is pretty neat.
Interesting thanks!
Hey guys,
Good work with your engine it’s looking pretty smart. Would you be able to give me an estimate of when you might bring out your map editor or a tutorial along those lines?
Tom
Hi Tom,
Thanks for the comments. Currently just working on some socket interaction so we can have a basic example of multiplayer in the video we are putting together.
If you have a look at the second part of this tutorial “Mouse Control on Isometric Canvas tiles” it will cover the interaction side and how to make a basic editor.
I’ll write up a tutorial for Saturday/Sunday on saving the map array to a SQLdb via PHP with the use of XML and then loading it back in. Once that’s complete everything needed for a rather simple map editor should be done (which is what GF has just now). I’ll then look into the best way to list tiles as their graphic rather than using drop down menus for selecting and so on. But by Sunday should the last step should be up.
Best,
Iain
Just wanted to tell you thank you for this tutorial! It was extremely helpful, only place I’ve found a javaScript specific isometric tutorial and again, thank you! Really looking forward to anything else you might have to share
These are great tutorials – keep them coming
Have you see this on youtube, this guy is doing a similar thing with JS and Canvas and seems to be quite far along…
http://www.youtube.com/view_play_list?p=AD27D42583D056D4
When I see the path finding he’s done it makes me wanna make a web version of transport tycoon! Oh I wish… can’t see a link to download the engine yet though so it’s probably still in the works.
Hi Iain,
My map tiles is 16×16, but how to add something liek House 100×100? or tree 50×35 or something like? maybe can simple example? or you plan tutorial?
Thanks
Hi John,
Varied width ground tiles would add a bit of complexity to this pretty simple method for creating a tile map… However I do plan on writing a tutorial or 2 this weekend/week about Object tiles and player movement/simple hitTest, if I can ignore work for a little longer I’ll see what else I can write up
Deal?
Sure! thanks for answer! very waiting.
and good luck.
Hello,
Sorry for question’s but i am have one more question.
Something like:
map = Array([0,0,0,...],…);
map1 = Array([1,1,1,...],…);
drawMap(){
…draw map first layer(grid, grass, …)
}
drawMap1(){
…draw map1 second layer(Tree, house, …)
}
This is correct method or bad?
Sorry for newbie questions. Good luck!
Hey John,
Have a look at http://glacialflame.com/2011/03/canvas-isometric-tutorial-add-a-player-and-throw-in-some-objects-pt2/
you can just add the draw function for the the second map into the first as long as it is after it. If you have different width objects though in your second map or layer shall we say, you would need to loop through ground(map) first and objects(map1) second in a seperate loop.
That help?
Best,
Iain
Hi,
Yes, thanks you very much!
And other tutorial’s is FANTASTIC!
Thanks, Good Luck!