Isometric tile coordinates

Started by
15 comments, last by AngleWyrm 16 years, 10 months ago
The third axis is vertical.

GDNet+. It's only $5 a month. You know you want it.

Advertisement
WorldCoordinate tile_origin(TileCoordinate tc)
{
return WorldCoordinate(
(tc.x - tc.y) * tile_width / 2,
(tc.x + tc.y) * tile_height / 2);
}

TileCoordinate tile_at(WorldCoordinate wc)
{
int x = (int)floorf( (float)wc.x / (float)tile_width);
int y = (int)floorf( (float)wc.y / (float)tile_height);
int rx = wc.x - (x * tile_width);
int ry = wc.y - (y * tile_height);

int f_x = x + y;
int f_y = y - x;

switch (Flat::index_surface(mouse_map,rx,ry).c)
{
case MouseMapColors::NORTHWEST :
f_x -= 1;
break;
case MouseMapColors::NORTHEAST :
f_y -= 1;
break;
case MouseMapColors::SOUTHEAST :
f_x += 1;
break;
case MouseMapColors::SOUTHWEST :
f_y += 1;
break;
default :
break;
}

return TileCoordinate(f_x,f_y);
}
--------------------------------------------------------------------------------
that code does not work for me, I don't know what I did but it does not work.
The way I did it using an isometric map was to, when the player clicked on the map:
-Redraw the map as normal, but to a hidden graphics surface and using a solid color for each tile. The color is based on each tile's XY coordinates, eg. tile (4,2) might be color (0,4,2).
-Get the color of the pixel where the player clicked.
-Translate that color back into coordinates.

That method gets around the problem of oddly-shaped tiles, and even works if you're using the isometric tiles for a pseudo-3D landscape where some of the tiles are "above" the "ground." Also it looks cool if you draw this hidden map to the actual screen instead of a hidden surface, using colors multiplied by ten or so to get a visible color gradient. 8)

I could post the code, but it's in Python.
Well thats why it wont work for me, I'm using VB6.0
trimetric, hm, maybe that is the reason why we found no resources for our problems searching for isometric tile coordinates with different angles.

Anyway

Few months ago I wrote such a "TileSystem" for our little browser game in Java.
If it would help you, I will send it to you or post the code here.

All you need for calculating positions of tiles (and translating mouse positions to cells) is the x and y coordinate of the tile in the array, the screen position and a method to calculate if the tile a point is inside the tile polygon (would be easy if you use squares :) ).

How that?
Forget to work only on the screen, positions are calculated for the complete map and then translated with the screen position (top left corner of the screen on the complete map) to the final screen positions (just because in the first post was something in relation with the mouse position).

For the other way round I used a binary search. I created a polygon for half the map, checked if it is in, if yes, split the polygon again, if not use the other side (described in a very short and rough way).
Complete TileSystem needs only a few int in the memory and you can easily change the angles but it does not support rotating (like in the Anno series with look north, east, south or west).

Why we haven't used mouse maps? Because applets just haven 16 MB RAM by default and we fought against every useless byte in the RAM.

That it works you can see here but

BEFORE YOU TRY
this is an prototype. You have to use firefox for trying (never found out why IE has problems with the param tag of an applet) and you need Java Runtime Enviroment 1.5 or higher and you have to accept the applets signature.
Language is german (just mentioned, never needed) because there is no server running behind producing producing products....
and may it takes time to load

http://orpheus.fh-hagenberg.at/students/mtd/mtd04007/Solgrad/

[Edited by - Woron kar DeDulle on May 30, 2007 3:27:46 AM]
@Deyja: how do I write such a cool code snippet in here?

well, got the ok so here is the code.

It is the Java tile system (if isometric, dimetric or trimetric, guess the last), no mouse maps are required for it, but this was done at the cost of performace. Eg if the player moves the mouse every time there has to be run through the map to find the pointed cell, but this is not called automatically, so be carefull with the implementation (if someone uses it).

Features:

* calculation of the position of the tiles on screen
(for that a screen position is needed with x and y position of the top left corner of the screen on the complete map)

* calculating the polygon representing the tile
* calculating a suggestion for the center of the tile
* calculating the boundingbox of a tile
* check if a given point is in the tile
(here again the point is meant as the point on the complete map, not on the screen)

* checks if mouse points on the map
* calculates the currently pointed cell
(again, the given point is the point on the complete map, not on the screen).


Suggestions or critics are always welcome and feel free to use if you like it and, if it is possible, mention Solgrad anywhere.

<code>
//----------------------------------------------------------------------------
// Solgrad TileSystem
//----------------------------------------------------------------------------
package shared.game;

import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;

public class TileSystem {

public static final int MAP_SIZE_LIMIT = 200;

//singleton elements
private static TileSystem curTS = null;

public static void createTileSystem(int angleA, int angleB,
short tileLength, short borderSize, int cellCountX, int cellCountY)
throws InvalidTileSystemValueException {

//checkValues
if (angleA < 0 || angleA > 90) {
throw new InvalidTileSystemValueException("angleA", "" + angleA,
"liegt zwischen 0 und 90 (beides inklusive).");
}

if (angleB < 0 || angleB > 90) {
throw new InvalidTileSystemValueException("angleB", "" + angleB,
"liegt zwischen 0 und 90 (beides inklusive).");
}

if (borderSize < 0) {
throw new InvalidTileSystemValueException("borderSize", "" + borderSize,
"ist größer oder gleich 0.");
}

if (cellCountX <= 0 || cellCountX > MAP_SIZE_LIMIT) {
throw new InvalidTileSystemValueException("cellCountX", "" + cellCountX,
"liegt zwischen 1 und " + MAP_SIZE_LIMIT + " (beides inklusive).");
}

if (cellCountY <= 0 || cellCountY > MAP_SIZE_LIMIT) {
throw new InvalidTileSystemValueException("cellCountY", ""+ cellCountY,
"liegt zwischen 1 und " + MAP_SIZE_LIMIT + " (beides inklusive). ");
}

if (curTS == null) {
curTS = new TileSystem(angleA, angleB, tileLength, borderSize,
cellCountX, cellCountY);
}

}

public static TileSystem get() {
return curTS;
}


//required values
private int angleA; //angle from the bottom to the left
private int angleB; //angle fromt the bottom to the right
private short tileLength; //length of a tile
private short borderSize; //distance between screen and map
private int cellCountX; //map size along x axis
private int cellCountY; //map size along y axis
// 0/0 is here the left edge of the map

//calculated help values
private int tCosA; //
private int tCosB; // cos of A, cos of B, sin of A, sin of B
private int tSinA; // multiplicated with the
private int tSinB; // tileLength

//calculated useable values
private int tileWidth;
private int tileHeight;
private Polygon masterTilePolygon;

private int mapWidth;
private int mapHeight;
private Polygon mapPolygon;


//map elements
private Polygon[][] polygonsOnMap;
private int[][] cellXPositionsOnMap;
private int[][] cellYPositionsOnMap;


private TileSystem(int angleA, int angleB, short tileLength,
short borderSize, int cellCountX, int cellCountY) {
this.angleA = angleA;
this.angleB = angleB;
this.tileLength = tileLength;
this.borderSize = borderSize;
this.cellCountX = cellCountX;
this.cellCountY = cellCountY;

//calculate help values
tCosA = (int)(Math.cos(Math.toRadians(this.angleA)) * this.tileLength);
tSinA = (int)(Math.sin(Math.toRadians(this.angleA)) * this.tileLength);
tCosB = (int)(Math.cos(Math.toRadians(this.angleB)) * this.tileLength);
tSinB = (int)(Math.sin(Math.toRadians(this.angleB)) * this.tileLength);

//calculate useable values
tileWidth = tCosB + tCosA;
tileHeight = tSinA + tSinB;

//mapHeight = tSinB * cellCountX + tSinA * cellCountY + 2 * borderSize;
//mapWidth = tCosA * cellCountY + tCosB * cellCountX + 2 * borderSize;
mapHeight = tSinB * cellCountX + tSinA * cellCountY + 2 * borderSize;
mapWidth = tCosA * cellCountY + tCosB * cellCountX + 2 * borderSize;

int[] xCos = {0, tCosB, tileWidth, tCosA};
int[] yCos = {tSinB, 0, tSinA, tileHeight};
masterTilePolygon = new Polygon(xCos, yCos, 4);

//calculating positions on map
cellXPositionsOnMap = new int[cellCountY][cellCountX];
cellYPositionsOnMap = new int[cellCountY][cellCountX];
for (int x = 0; x < cellCountX-1; x++) {
for (int y = 0; y < cellCountY-1; y++) {
cellXPositionsOnMap[y][x] = calcTilePosX(x,y);
cellYPositionsOnMap[y][x] = calcTilePosY(x,y);
}
}

//calculating tile polygons
polygonsOnMap = new Polygon[cellCountY][cellCountX];
for (int y = 0; y < cellCountY; y++) {
for (int x = 0; x < cellCountX; x++) {
polygonsOnMap[y][x] = calcTilePolygon(x,y);
}
}

int[] xPos = { getTilePolygon(0,0).xpoints[0],
getTilePolygon(cellCountX-1, 0).xpoints[1],
getTilePolygon(cellCountX-1, cellCountY-1).xpoints[2],
getTilePolygon(0, cellCountY-1).xpoints[3]};

int[] yPos = { getTilePolygon(0,0).ypoints[0],
getTilePolygon(cellCountX-1, 0).ypoints[1],
getTilePolygon(cellCountX-1, cellCountY-1).ypoints[2],
getTilePolygon(0, cellCountY-1).ypoints[3]};

mapPolygon = new Polygon(xPos, yPos, 4);
}


//getters for required values
public int getAngleA() { return angleA; }
public int getAngleB() { return angleB; }
public short getBorderSize() { return borderSize; }
public short getTileLength() { return tileLength; }
public Polygon getTilePolygon(int x, int y) { return polygonsOnMap[y][x]; }
public int getTileWidth() { return tileWidth; }
public int getTileHeight() { return tileHeight; }
public int getTilePosX(int x, int y) { return cellXPositionsOnMap[y][x]; }
public int getTilePosY(int x, int y) { return cellYPositionsOnMap[y][x]; }
public Polygon getMapPolygon() { return mapPolygon; }
public int getMapCellSizeWidth() { return cellCountX; }
public int getMapCellSizeHeigth() { return cellCountY; }
public int getMapHeigth() { return mapHeight; }
public int getMapWidth() { return mapWidth; }


//********************************************************
// cell functionality
//********************************************************
//x and y are the positions in the array
public int calcTilePosX(int x, int y) {
//return borderSize + (x * tCosB * tileLength) + (y * tCosA * tileLength);
//if (x == 0 && y == 0) System.out.println(x + "/" + y + " X: " + (borderSize + (x * tCosB) + (y * tCosA)));
return borderSize + (x * tCosB) + (y * tCosA);

}

//x and y are the positions in the array
public int calcTilePosY(int x, int y) {
//return borderSize + (cellCountX - x - 1) * (tSinB * tileLength) + (y * (tSinA * tileLength));
//if (x == 0 && y == 0) System.out.println(x + "/" + y + " Y: " + (borderSize + (cellCountX - x - 1) * (tSinB) + (y * (tSinA))));
return borderSize + (cellCountX - x - 1) * (tSinB) + (y * (tSinA));
}

//x and y are the positions in the array
public Polygon calcTilePolygon(int x, int y) {

int[] xCos = {masterTilePolygon.xpoints[0] + getTilePosX(x,y),
masterTilePolygon.xpoints[1] + getTilePosX(x,y),
masterTilePolygon.xpoints[2] + getTilePosX(x,y),
masterTilePolygon.xpoints[3] + getTilePosX(x,y)};

int[] yCos = {masterTilePolygon.ypoints[0] + getTilePosY(x,y),
masterTilePolygon.ypoints[1] + getTilePosY(x,y),
masterTilePolygon.ypoints[2] + getTilePosY(x,y),
masterTilePolygon.ypoints[3] + getTilePosY(x,y)};

return new Polygon(xCos, yCos, 4);
}


//x and y are the positions in the array
public Rectangle calcTileBoundingBox(int x, int y) {
return new Rectangle(getTilePosX(x,y),getTilePosY(x,y), tileWidth, tileHeight);
}

//x and y are the positions in the array
public Point calcTileCenter(int x, int y) {
return new Point(getTilePosX(x,y) + tileWidth/2, getTilePosY(x,y) + tileHeight/2);
}

public boolean checkIfInTile(int x, int y, Point mousePosOnMap) {

if (!calcTileBoundingBox(x,y).contains(mousePosOnMap)) {
return false;
}

return getTilePolygon(x,y).contains(mousePosOnMap);
}


//*************************************************************
// map functionality
//*************************************************************

//functionality

public boolean validXIndex(int x) { return x > 0 && x < cellCountX; }
public boolean validYIndex(int y) { return y > 0 && y < cellCountY; }


//mouse position on the map has to be calculated outside
public boolean checkIfInMap(Point mousePosOnMap) {
return mapPolygon.contains(mousePosOnMap);
}

public Polygon calcMapSplitPolygon(int xStart, int xStop, int yStart, int yStop) {

int[] xPos = {
getTilePolygon(xStart, yStart).xpoints[0],
getTilePolygon(xStop, yStart).xpoints[1],
getTilePolygon(xStop, yStop).xpoints[2],
getTilePolygon(xStart, yStop).xpoints[3]
};

int[] yPos = {
getTilePolygon(xStart, yStart).ypoints[0],
getTilePolygon(xStop, yStart).ypoints[1],
getTilePolygon(xStop, yStop).ypoints[2],
getTilePolygon(xStart, yStop).ypoints[3]
};

return new Polygon(xPos, yPos, 4);
}

//mouse position on the map has to be calculated outside
public Point calcCurrentCellPos(Point mousePosOnMap) {

if (!mapPolygon.contains(mousePosOnMap)) { // if point is outside of map
return null;
}

// binary cell search, search xPos
int left = 0;
int right = cellCountX;
int searchPos;

while (right > left) {
searchPos = (right - left) / 2;
if (calcMapSplitPolygon(left, left + searchPos, 0, cellCountY).contains(mousePosOnMap)) {
right = left + searchPos;
} else {
left = left + searchPos + 1;
}
}
int xCo = left;

// search yPos
left = 0;
right = cellCountY;

while (right > left) {
searchPos = (right - left) / 2;
if (calcMapSplitPolygon(xCo, xCo, left, left + searchPos).contains(mousePosOnMap)) {
right = left + searchPos;
} else {
left = left + searchPos + 1;
}
}
int yCo = left;

return new Point(xCo, yCo);
}

public static Polygon movePolygon(Polygon g, int x, int y) {
Polygon h = new Polygon(g.xpoints, g.ypoints, g.npoints);
h.translate(x,y);
return h;
}
}
</code>
An interesting article on hex map coordinates
--"I'm not at home right now, but" = lights on, but no ones home

This topic is closed to new replies.

Advertisement