iDevGames Forums

Full Version: Isometric Tile Plotting
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2



I am new to this forum (which is great by the way) and I am working on a game with an isometric viewpoint. I have worked out (Google is my friend) how to draw and click tiles in a staggered viewpoint - see image below:

[Image: staggered_Iso.gif]

The (simplified) routine I use to calculate the x,y coordinates for where to draw the tiles is (note, this is REALbasic code):
for y = 0 to (rows - 1)
      for x = 0 to (columns - 1)
        ' Calculate MapX
        MapX = x * TileWidth
        if isOdd(y) then ' shift MapX to the right
          MapX = MapX + (TileWidth/2)
        end if
        ' Calculate MapY
        MapY = y * (TileHeight/2)
        ' Add this tile to the array
        MapArray(x, y) = theTile
      next x
    next y
However, I want to know how to draw a rotated isometric viewpoint like so:

[Image: rotated_Iso.gif]

Any suggestions? I have tried for hours to work out where to draw the tiles to. I want to start drawing from (0,0) and I have been trying to figure the coordinates that the tiles should be put as we go from (0,0) to (0,1) to (0, 2), etc but I can't work out the algorithm. It would seem I need to do the following (assuming 64x32 tiles):

(0,0) = (0,0)
(0,1) = (-32, 16)
(0,2) = (-64, 32)

etc, and

(1,0) = (32, 16)
(2,0) = (64, 32)

Where negative x coordinates represent points to the left of the mid point of the x-axis (where the map is draw from).

Any help would be greatly appreciated.


This comes out of the Isometric Game Programming with DirectX 7 book. Its C++, I dont know BASIC:

PlotX and PlotY are the coords for placing the tile apparently. I dont know if the starting (0,0) drawing coords are in the middle or not.

for( x=0; x<ROWS; x++ ) {
    for( y=0; y<COLUMNS; y++ ) {
        plotX = (x-y)*TileWidth/2;
        plotY = (x-y)*TileHeight/2;
Its like when people say someone is programming by smashing things around with a huge hammer, you need to just get a bit of paper and work it out calmly.

In standard computer coordinates, not the inverted, confusing and wrongly chosen quartz coordinates.

As the x coordinate increases the tiles move diagonally downwards and to the right,
x_tile = x*64
y_tile = -x*32

when the y coordinate increases the tiles move diagonally downwards and to the left
x_tile = -y*64
y_tile = -y*32

you add them together to get

x_tile = (x-y)*64
y_tile = -(y+x)*32

that will give you the tile (0, 0) at (0, 0) and the tile (1, 1) at (0, -64)
You will probably want your final equation to be

x_tile = screen_width/2-32+(x-y)*64
y_tile = screen_height-(y+x)*32

cheatdeath Wrote:plotX = (x-y)*TileWidth/2;
plotY = (x-y)*TileHeight/2;

did you think about tile (0, 0) and tile (1, 1) being drawn in the same place. I hope thats a typo and not an error in the book :o


Thanks unknown for your reply,

Unfortunately, it doesn't seem to be correct Sad

The formulae (ignoring the screen width/height offsets):
x_tile = (x-y)*64
y_tile = (y+x)*32

gives the following output:

Tile ------> Coords ------> Should be
(0, 0) -----> (0, 0) --------> (0, 0)
(0, 1) -----> (-64, 32) -----> (-32, 16)
(0, 2) -----> (-128, 64) ----> (-64, 32)
(1, 0) -----> (64, 32) ------> (32, 16)
(2, 0) -----> (128, 64) -----> (64, 32)

Frustrating isn't it? Blink

That code can't work - Unknown was right, most of the tiles get drawn in the same place!

use tile_width and tile_height /2.
*smacks self on head*


Thanks unknown!

Now I need to get to work on tile selection with this new viewpoint.

Thanks again!



Okay, please don't think that i'm not doing any work but....

I am now struggling with tile selection in this rotated viewpoint.

With the staggered viewpoint (see my first picture) I am able to work out where a person has clicked by using a "MouseMap" - see image below:

[Image: mouseMap_64x32.png]

I divide the screen up into "regions" - each region is 64x32 pixels (i.e. tileWidth x tileHeight). I work out which region the mouse is in using the following code (you can ignore scrollOffset - it's only relevant if the map has been scrolled):

regionX = (mouseX - xScrollOffset)\tileWidth
    regionY = ((mouseY - yScrollOffset)\tileHeight) * 2

Once I know what region the mouse cursor is - I calculate it's coordinates (mouseMapX, mouseMapY) within the region (for instance, if it's in the top left most corner the coordinates would be 0,0) using this code:

MouseMapX = (mouseX - xScrollOffset) mod tileWidth
  MouseMapY = (mouseY - yScrollOffset) mod tileHeight

Once I know these coordinates, I calculate which region (white, red, green, blue or yellow) the cursor is in on the mouseMap. For each coloured region there is a set regionOffset (e.g. for red I decrease regionX by 1 and regionY by 1). At this point I can calculate the tile we are hovering over using the code:

tileX = regionX + regionDX
  tileY = regionY + regionDY

I know this sounds convoluted but I was really struggling a way to work out which tile the mouse was hovering over using maths Blush !

Needless to say, I think this approach is probably rubbish and I would appreciate any advice on a better approach. Not only this, but I cannot work out how to make this approach work with a rotated view (as you can't simply slice the map into a grid because the tiles are arranged in a diamond format now and not in straight lines!).

I hope this makes some sense - I have just spent ages doing the following maths with the hope that I could calculate the tile I was over (these calculations are derived from unknown's method of plotting said tiles):

x_tile = screen_width/2-32+(x-y)*(tileWidth/2)
y_tile = screen_height-(y+x)*(tileHeight/2)

simplified is (for a 64x32 tile, ignoring the offsets):

x_tile = (x-y)*32
y_tile = (y+x)*16


x_tile/32 = x-y
x= x_tile/32 + y

y_tile/16 = x+y
y_tile/16 = x_tile/32 + 2y
2y = y_tile/16 - x_tile/32
y = y_tile/32 - x_tile/64
y = 2y_tile - x_tile/64

x = x_tile/32 + 2y_tile - x_tile/64
x = x_tile/64 + y_tile/32

I have tried these equations (I know x_tile and y_tile as these are the mouse coordinates, right?).

I am so confused!

Thanks in advance,

I'm not sure whether this is possible with RealBASIC (I've only used it in OpenGL), but an alternative approach to convoluted maths is to use colour-picking; render a copy of the tiles to an offscreen buffer, where each tile has a colour which can be easily interpreted as a coordinate (i.e. tile 0,0 would be #000000, tile 1,0 #000100, tile 1,1 #000101, outside the tiles would be #ff0000); then, when the user clicks the mouse, simply grab the colour of the pixel at the coordinates of the mouse click and interpret the pixel's colour to arrive at the tile coordinates.
What you need is to map your coordinate system. Basically, you need to multiply your (mouseX,mouseY) vector by a matrix that maps your diamond ring to an axis aligned square grid. Judging from the images, your matrix should be applying both rotation and shear.

So, to rotate & shear your (mouseX,mouseY) vector (the mouse position) to map it to your board, simply multiply it by the (fixed and pre-computed) "rotation & shear" matrix. To obtain this matrix, you need to calculate each one separately and then multiply them.

Here is a page describing rotation & shear matrices (among others):

A member of iDevGames also has a nice page about this, but I can't seem find the link, sorry.

Anyway, once you finally have your point mapped from screen-space to board-space, all you need to do is:
tileX = mappedMouseX%lengthOfTileSide;
tileY = mappedMouseY%lengthOfTileSide;

That way you can reference your block in a 2D array like this:

Edit: Actually, don't bother with the above, here what it boils down to:

Go to the very end of that article, "Screen to Map Transformation", it gives you the exact formulas for the map to screen and screen to map transformations.
PowerMacX Wrote:A member of iDevGames also has a nice page about this, but I can't seem find the link, sorry.

This? (ThemsAllTook's tutorials)
Yes, that's the one! (but see my edit to my previous post)


Thanks for the link.

Bearing in mind that,although I did pure maths at A-level, 6 years of boozy medical school has left slightly less sharp than I used to be Blush !

Looking at the article at bookofhook, i'm not sure what mx, my, sx, sy are. I'm guessing that mx/my are the column/row of the tile (mapX, mapY) and that sx, sy are the mouseX and mouseY coordinate - right?

I hate it when 'simple' things seem overly complicated!

Yes, sx,sy are screenX,screenY (or mouseX,mouseY) and mx,my are mapX,mapY.

Some notes: the formula you want is the last one, but it can be simplified even further, to this:

mapX = screenY/32 + screenX/64
mapY = screenY/32 - screenX/64

That is, the formula in the page for mX actually says:
mX = (64*sY + 32*sX)/(64*32)

but that can be rewritten to just:
mx = sY/32 + sX/64

as in the code above (the same applies for mY)

Also, this assumes that screenY increases when you move your mouse down, and that when the mouse is at (0, 0) you are in the tile (0, 0). Of course, since the tile won't be at (0, 0) on the screen, remember to translate (subtract) its real coordinates before using screenX/screenY. That is, if the top corner of your (0, 0) tile is at (100, 100) on the screen, you should subtract 100 from screenX and from screenY before applying the formula.

Finaly, mX and mY should be integers, you should truncate the result of the formulas if your language doesn't do it automatically and, you have to check for bounds (values < 0 are outside your map, and > num. horizontal/vertical tiles are also outside). Here are some screen vs. map results so you can figure out what it is doing:

screenX    screenY    mapX    mapY
0    0    0    0
16    0    0.25    -0.25 (outside the map!)
32    0    0.5    -0.5 (outside the map!)
0    16    0.5    0.5
0    48    1.5    1.5
32    32    1.5    0.5
64    48    2.5    0.5
96    64    3.5    0.5

As you can see, if you truncate the mapX & mapY values, you get the correct tile. For instance, lets say that your first tile has its top corner on the screen at x:100,y:200. Then, if your mouse is at (100, 216) it will be exactly in the center of this first tile. In your code:

screenX = mouseX - 100         'so, screenX = 0
screenY = mouseY - 200         'so, screenY = 16

From the table, screenX,Y with (0, 16) gives you a mapX,Y of (0.5, 0.5) which means the exact middle of tile (0,0). If you then move your mouse 32 pixels down from there (mouseY = 248 --> screenY = 48), you'll get mapX: 1.5 mapY: 1.5, meaning you are now in the exact middle of the tile (1,1). Likewise, (32,32) gives you (1.5, 0.5) which is the middle of tile (1,0), and so on.

As I mentioned, truncate your mapX, mapY values to get the tile. The decimal part only tells you where, within a tile, is the mouse positioned, with .0 being a corner and .5 being the exact middle.


I'm hoping that someone can help me here. This has been bothering me for a long time.

I can't get this to work correctly. When I'm in the top left of the tile it's correct, but if I'm in the bottom left or bottom right of the first tile it goes to the next tiles.

Quote:From the table, screenX,Y with (0, 16) gives you a mapX,Y of (0.5, 0.5) which means the exact middle of tile (0,0).

How is 0.16 in the middle of the middle of the first tile? Wouldn't it be 32,16 since the tiles are 64x32? The reason this doesn't make sense to me I think is why my code is acting as it is.

Here is what I use to draw:
//draw the map
                drawX = col * (TILE_W/2) - row * (TILE_W/2) + startX;
                drawY = col * (TILE_H/2) + row * (TILE_H/2) + startY;
                draw_sprite(buffer, map[row][col].image, drawX, drawY);

And here is what I use to map mouse to map:
if(mouse_b & 1)
            screenX = mouse_x - startX;
            screenY = mouse_y - startY;

                        //these are floats I did for testing purposes
            testCol = screenY/TILE_W + screenX/TILE_H;
            testRow = screenY/TILE_W - screenX/TILE_H;

                       //after this if I'm in bottom right of the first tile testCol = 1, which it should = 0

                       //these are ints so they get truncated
            mapCol = testCol;
            mapRow = testRow;

            if(mapCol > -1 && mapCol < MAP_COL && mapRow > -1 && mapRow < MAP_ROW)
                map[mapRow][mapCol].image = highlighted;
Quote:How is 0,16 in the middle of the middle of the first tile? Wouldn't it be 32,16 since the tiles are 64x32?

(0, 0) would be the top corner of the top tile. The left corner of that 64x32 tile would be at (-32, 16), the right corner at (32, 16) -hence 64 pixels wide- and the bottom corner would be at (0, 32) -that is, 32 pixels high. Does that make sense? Smile
Pages: 1 2
Reference URL's