## Isometric Tile Plotting

Garry
Unregistered

Post: #1
Hello,

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:

The (simplified) routine I use to calculate the x,y coordinates for where to draw the tiles is (note, this is REALbasic code):
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:

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.
cheatdeath
Unregistered

Post: #2
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.

Code:
```for( x=0; x<ROWS; x++ ) {     for( y=0; y<COLUMNS; y++ ) {         plotX = (x-y)*TileWidth/2;         plotY = (x-y)*TileHeight/2;     } }```
Sage
Posts: 1,403
Joined: 2005.07
Post: #3
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,
so
x_tile = x*64
y_tile = -x*32

when the y coordinate increases the tiles move diagonally downwards and to the left
so
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

Sir, e^iÏ€ + 1 = 0, hence God exists; reply!
Garry
Unregistered

Post: #4

Unfortunately, it doesn't seem to be correct

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?

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

Garry,
Sage
Posts: 1,403
Joined: 2005.07
Post: #5
use tile_width and tile_height /2.

Sir, e^iÏ€ + 1 = 0, hence God exists; reply!
Garry
Unregistered

Post: #6
Thanks unknown!

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

Thanks again!

Garry
Garry
Unregistered

Post: #7
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:

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):

Code:
```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:

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:

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 !

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

thus,

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!

Garry
Moderator
Posts: 713
Joined: 2002.04
Post: #8
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.

Mark Bishop
--
Student and freelance OS X & iOS developer
Moderator
Posts: 776
Joined: 2003.04
Post: #9
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):
http://www.cc.gatech.edu/gvu/multimedia/...ransf.html

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

That way you can reference your block in a 2D array like this:
Code:
`tile[tileX][tileY]`

Edit: Actually, don't bother with the above, here what it boils down to:
http://www.bookofhook.com/Article/GameDe...opmen.html

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.
Moderator
Posts: 713
Joined: 2002.04
Post: #10

This? (ThemsAllTook's tutorials)

Mark Bishop
--
Student and freelance OS X & iOS developer
Moderator
Posts: 776
Joined: 2003.04
Post: #11
Yes, that's the one! (but see my edit to my previous post)
Garry
Unregistered

Post: #12

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 !

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!

Garry,
Moderator
Posts: 776
Joined: 2003.04
Post: #13
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:

Code:
```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:

Code:
```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:

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.
Piller187
Unregistered

Post: #14
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:
Code:
```//draw the map         for(row=0;row<MAP_ROW;row++)         {             for(col=0;col<MAP_COL;col++)             {                 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:
Code:
```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;         }```
Moderator
Posts: 776
Joined: 2003.04
Post: #15
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?