[2d] Drawing Isometric game worlds

What is the correct way to draw isometric tiles in a 2D game?

I've read references (such as this one) that suggest the tiles be rendered in a way that will zig-zag each column in the 2D array representation of the map. I imagine that they should be drawn more in a diamond fashion, where what gets drawn to the screen relates more closely to what the 2D array would look like, just rotated a little.

Are there advantages or disadvantages to either method?

This question is related to 2d isometric

The answer is


You could use euclidean distance from the point highest and nearest the viewer, except that is not quite right. It results in spherical sort order. You can straighten that out by looking from further away. Further away the curvature becomes flattened out. So just add say 1000 to each of the x,y and z components to give x',y' and z'. The sort on x'*x'+y'*y'+z'*z'.


Coobird's answer is the correct, complete one. However, I combined his hints with those from another site to create code that works in my app (iOS/Objective-C), which I wanted to share with anyone who comes here looking for such a thing. Please, if you like/up-vote this answer, do the same for the originals; all I did was "stand on the shoulders of giants."

As for sort-order, my technique is a modified painter's algorithm: each object has (a) an altitude of the base (I call "level") and (b) an X/Y for the "base" or "foot" of the image (examples: avatar's base is at his feet; tree's base is at it's roots; airplane's base is center-image, etc.) Then I just sort lowest to highest level, then lowest (highest on-screen) to highest base-Y, then lowest (left-most) to highest base-X. This renders the tiles the way one would expect.

Code to convert screen (point) to tile (cell) and back:

typedef struct ASIntCell {  // like CGPoint, but with int-s vice float-s
    int x;
    int y;
} ASIntCell;

// Cell-math helper here:
//      http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511
// Although we had to rotate the coordinates because...
// X increases NE (not SE)
// Y increases SE (not SW)
+ (ASIntCell) cellForPoint: (CGPoint) point
{
    const float halfHeight = rfcRowHeight / 2.;

    ASIntCell cell;
    cell.x = ((point.x / rfcColWidth) - ((point.y - halfHeight) / rfcRowHeight));
    cell.y = ((point.x / rfcColWidth) + ((point.y + halfHeight) / rfcRowHeight));

    return cell;
}


// Cell-math helper here:
//      http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds/893063
// X increases NE,
// Y increases SE
+ (CGPoint) centerForCell: (ASIntCell) cell
{
    CGPoint result;

    result.x = (cell.x * rfcColWidth  / 2) + (cell.y * rfcColWidth  / 2);
    result.y = (cell.y * rfcRowHeight / 2) - (cell.x * rfcRowHeight / 2);

    return result;
}

If you have some tiles that exceed the bounds of your diamond, I recommend drawing in depth order:

...1...
..234..
.56789.
..abc..
...d...

Real problem is when you need draw some tile/sprites intersecting/spanning two or more other tiles.

After 2 (hard) months of personal analisys of problem I finally found and implemented a "correct render drawing" for my new cocos2d-js game. Solution consists in mapping, for each tile (susceptible), which sprites are "front, back, top and behind". Once doing that you can draw them following a "recursive logic".


Either way gets the job done. I assume that by zigzag you mean something like this: (numbers are order of rendering)

..  ..  01  ..  ..
  ..  06  02  ..
..  11  07  03  ..
  16  12  08  04
21  17  13  09  05
  22  18  14  10
..  23  19  15  ..
  ..  24  20  ..
..  ..  25  ..  ..

And by diamond you mean:

..  ..  ..  ..  ..
  01  02  03  04
..  05  06  07  ..
  08  09  10  11
..  12  13  14  ..
  15  16  17  18
..  19  20  21  ..
  22  23  24  25
..  ..  ..  ..  ..

The first method needs more tiles rendered so that the full screen is drawn, but you can easily make a boundary check and skip any tiles fully off-screen. Both methods will require some number crunching to find out what is the location of tile 01. In the end, both methods are roughly equal in terms of math required for a certain level of efficiency.