Creating an Isometric World with EaselJS

Games have many different views – top-down, side-scrolling, 3D, isometric, etc etc.  Today, I will be writing about how to set up an isometric view using Javascript and the EaselJS library.  Isometric view is a 2D representation of 3D.  In 3D, objects have vanishing points while they move, and their dimensions and scales change.  In isometric view, this does not happen; objects do not have vanishing points and lines remain parallel and proportionate to one another.

1. Creating an isometric tile

To create an isometric tile, follow these simple steps:

  1. Draw a square
  2. Rotate the square by 45 degrees
  3. Set the square’s height to 50%

That’s it!  An isometric tile’s height is half of its width.

creating-isometric-tile

2. Adding the tile to the world

An isometric view basically contains rows and columns of tiles.  This means having two for loops, one in the other.  In EaselJS, for a 4 x 4 grid with tiles that have 130-pixel width and 65-pixel height, you’ll end up with something like this:

for (i = 0; i < 4; i++) {
  for (j = 0; j < 4; j++) {
    bmp = new createjs.BitmapAnimation(img);
    bmp.x = (j-i) * x;
    bmp.y = (i+j) * y;
    bmp.regX = 65;
    bmp.regY = 32.5;
    context.stage.addChild(bmp);
  }
}

The 4’s in the for loop come from the number of rows and columns in the grid (4 x 4).  I used BitmapAnimation in case we want to make an assortment of tiles (explained in Section 5).  regX and regY is for the registration point of the tile; we want to make it at the center of the tile in order for them to be placed in a grid-like pattern.  The tile is 130×65, so half of 130 is 65, and half of 65 is 32.5.  This will make the registration point at its center.

isometric-view

You’ll notice that the tiles have a “side view” (the brown part).  I’ve just added that for cosmetic purposes to give the illusion that we can see the ground underneath the grass.

3. Creating an assortment of isometric tiles

Let’s say we want many different types of tiles – grass, water, gravel, sand, etc etc.  Go wild with your tiles!  But for the purpose of this blog post, I’ll keep it simple and have a grass tile, a water tile, and tiles that have a bit of both.  As for the numbers underneath each tile, I’ll explain those in the next sections.

assorted-isometric-tiles

4. Creating an external tile map data file

Ideally, we’d want the tile information in an external file.  This will make it easier when our world, or tile map, is huge and has an assortment of tiles.  I’ve created a JSON file to represent the rows of the tile map, and which frame is in each tile.  The rows are the keys and the list of each tiles’ frames in each row are the values:

{
  “main”: {
    “0”: [“1″,”5″,”5″,”2”],
    “1”: [“8″,”0″,”0″,”6”],
    “2”: [“8″,”0″,”9″,”6”],
    “3”: [“4″,”7″,”7″,”3”]
  }
}

So if you map it with the image in Section 3, you can expect the tile at (0,0) to have water at its northern corner and grass in the rest of its tile (frame 1 in the Section 3 image).  We also expect to see the water tile in (2,2), which is frame 9 of the image.  We’ll see full grass tiles in (1,1), (1,2), and (2,1), which is frame 0 of the image.  Hopefully, you get the picture!

5. Setting up the world using the tile map data

So let’s revise the code a bit from Section 2 to use our tile map data.  But first, we have to load the external tile map data file.  Let’s assume we’ve called the file “game-map.json” and it’s in the “data” directory.  And remember our function scoping in Javascript, hence the this to that reference!

var that = this;
$.getJSON('data/game-map.json', function(data) {
  that.mapData = data['main'];
  that.createTileMap(img, that.mapData);
});

And let’s put it together with our previous code.  Note the “bmp.currentFrame” line of code:

createTileMap : function(img, data) {
  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      bmp = new createjs.BitmapAnimation(img);
      bmp.x = (j-i) * x;
      bmp.y = (i+j) * y;
      bmp.regX = 65;
      bmp.regY = 32.5;
      bmp.currentFrame = data[i][j];
      context.stage.addChild(bmp);
    }
  }
}

And the results, just as we’ve predicted!

Screen Shot 2013-02-24 at 10.38.42 PM

EaselJS (with Backbone.js)

In my last post, I wrote about gameQuery; since then, I have tried out EaselJS, a similar Javascript library. Unlike gameQuery, however, EaselJS uses HTML5 canvas; gameQuery uses DOM manipulation.

I tried building the same game from my last post with EaselJS and Backbone.js. To my surprise, working with EaselJS was somewhat similar to coding in ActionScript 3 – there was a stage (the root level container for a display list) and there was”addChild(),” which is used to add display objects to containers. In fact, many of the methods in Container reminded me of AS3’s DisplayObjectContainer, so coming from a Flash background, I found EaselJS’s concept of a display list and working with it easy to grasp.

Left: EaselJS in a Backbone.js View; Right: gameQuery in a Backbone.js View
Left: EaselJS in a Backbone.js View; Right: gameQuery in a Backbone.js View

In the above code screenshot, it took less than half as many lines in gameQuery to load images.  However, from a readability perspective, I could understand what’s going on in the EaselJS code without having to look it up in the documentation.  Also, I like the flexibility of EaselJS in that I have the ability to add event listeners for when the images have been loaded.  gameQuery was more about setting images and their properties to certain DOM nodes.  Furthermore, I would rather go with EaselJS, since it is still being actively updated and maintained.  And, last but not least, EaselJS is part of a suite of Javascript libraries – CreateJS – which has libraries for tweens, sounds, and preloaders.  This means easy integration with these other libraries.  There is even a way to integrate EaselJS with Box 2D for physics, which I have yet to explore.

So in my quest to find a Javascript library for graphics and animation, I will be going with EaselJS.  I’ve quickly looked at Crafty, but its documentation seemed a bit lacking, and its features seemed comparable with EaselJS… although it isn’t in a suite of other library goodies like EaselJS.  🙂

Check out my source code for EaselJS with Backbone.js here.