Tuesday, October 25, 2011

Isometric Coordinate Calculation Tutorial

Here we’ll look at how to calculate a map coordinate from a screen coordinate in an isometric game.



First we’ll need to look at some definitions and assumptions about our isometric system.

Coordinate Systems
Tile Array Map
We are calculating coordinates in two coordinate systems.  The first is the Map coordinate system.  Imagine this as a square map drawn on paper.  This is the coordinates from our map, which is a two dimensional array of squares, which we call cells.  To describe a map cell we use an (x,y) coordinate.  The first tile at (0,0) is the top left cell.  Increasing the x value describes map cells to the right of the origin.  Increasing the y value describes map cells below the origin.



Isometric Tile Array
The second coordinate system is the isometric coordinate system, also often called the screen coordinate space.  This is the same as the map coordinate system but rotated 60(ish) degrees to the right, as shown here.





In the case of my TileEngine game platform the isometric tiles are twice as wide as they are high, and both of these numbers are a power of two (this will make the calculations faster).  For example, here is a tile from the default TileSet size that I use which is a diamond 64 pixels wide and 32 pixels high.  I call these the TileWidth and the TileDepth respectively.  (As an aside; I call it the TileDepth because I use the name TileHeight for tiles which have a height component such as wall tiles).

For example, if we have a floor tile like this :
Single Isometric Tile






Tile Width and Depth

The following shows the TileWidth and TileDepth dimensions.











Tile Width, Depth and Height


If the tile has a height component (such as your Actor/Avatar sprites or wall sprites) then you would also have a TileHeight dimension like this:

When you are working with Tiles that have a height component you will need to subtract the TileHeight value from the ScreenY coordinate for these.




Map to Isometric (screen) coordinate conversion.
To calculate where on the screen a tile should be displayed we need to store the screen coordinates of the origin map cell (0,0), called the OriginX and OriginY.  These screen coordinates don’t have to even be visible on the screen.  For instance, if the origin cell is above and to the left of the screen then both of these values will be negative.
Two equations calculate the screen location for a given map cell:


ScreenX = OriginX – (MapY * TileWidth/2) + (MapX * TileWidth/2) – (TileWidth/2)
ScreenY = OriginY + (MapY * TileDepth/2) + (MapX * TileDepth/2)

As you move the view around, simply modify the OriginX and OriginY values.
If your development platform works upside down to the way that I do things (ie: your screen origin (0,0) is at the bottom left of your screen) then you will need to use the negative of any ScreenY values.  I would also add the height of the screen to move the screen origin to the top, something like this:


ScreenY = (- ScreenY + ScreenHeight)
(This isn't tested, but I think it should be about right.)

We can also calculate a point within each map cell for more accuracy.  I divide each map cell into an imaginary grid of 32x32 (or more accurately a grid of TileDepth by TileDepth).  The point in the centre of the grid is (0,0).  The point on the cell at the top left is (-16,-16), and the bottom right is (16,16).  This can be used to calculate where on the cell an actor is located.  If we didn’t use this then they would “jump” between cells rather than slowly move from one to another.  We call these values DeltaX and DeltaY.
If the player character sprite is locked in the centre of the screen then we can use a pair of these delta values.  To convert from map coordinate delta values to isometric coordinate delta values we use the following equations:

IsoDeltaX = (MapDeltaX – MapDeltaY)
IsoDeltaY = (MapDeltaY + MapDeltaX) / 2


These can then be added to the ScreenX and ScreenY values respectively to find the correct sprite position on the screen.

Isometric (screen) to Map coordinate conversion.
Often you need to find a Map coordinate from a screen coordinate, for example when the user clicks on the screen you wish to know the map cell which they clicked.  This calculation is somewhat more complex. 
First we need to get the x and y coordinates of the screen position relative to the scroll and centre coordinates:

X0 = ScreenX – (OriginX – IsoDeltaX)
Y0 = ScreenY – (OriginY – IsoDeltaY)


From these values we can start to calculate the Map coordinates:


X = Y0 + (X0 / 2)
Y = Y0 – (X0 / 2)


These are the map coordinates multiplied by the TileDepth grid size, so we’ll divide both of these X and Y values by TileDepth to get the actual Map coordinates:


MapX = X / TileDepth
MapY = Y / TileDepth


We should also be able to calculate the MapDeltaX and MapDeltaY values by taking the remainder of the previous calculation:


MapDeltaX = X \ TileDepth
MapDeltaY = Y \ TileDepth




Delta Values.
I knew when I wrote this tutorial that the Delta values were a little confusing :-/

MapDeltaX is the delta in map space and DeltaX is it converted to Isometric (or screen) space (I think I've also called this IsoDeltaX). They help when mapping coordinates within cells and allow smooth scrolling and moving (rather than "teleporting" between cells).

The delta values are distances from the centre of each map cell.

For instance if we have an actor standing in the centre of a map cell then their DeltaX and DeltaY = (0,0), however if they are still standing on that cell, but have moved slightly closer to the next X cell at say 1 pixel at a time, then their DeltaX/Y = (1,0). Moving further it would be (2,0) and further increasing to TileWidth/2, ie: (16, 0). At this point their MapX coordinate would jump to the next cell and their Delta coords would become (-15,0).

For instance if we are walking smoothly from Map Coord (10,10) to (11,10) the sequence is as follows:
MapX MapY MapDeltaX MapDeltaY
10 10 0 0
10 10 2 0
10 10 4 0
10 10 6 0
10 10 8 0
10 10 10 0
10 10 12 0
10 10 14 0
10 10 16 0
11 10 -14 0
11 10 -12 0
11 10 -10 0
11 10 -8 0
11 10 -6 0
11 10 -4 0
11 10 -2 0
11 10 0 0
Does that make sense?

No comments:

Post a Comment