5
\$\begingroup\$

I'm trying to implement mouse interaction in a 2D isometric game, but I'm not sure if it's possible given the coordinate system used for tile maps in the game.

I've read some helpful things like this. However, this game's coordinate system is "jagged" (for lack of a better word), and looks like this:

Example

Is it even possible to map mouse coordinates to this successfully, since the y-axis can't be drawn on this tile-map as a straight line?

I've thought about doing odd-y-value translations and even-y-value translations with two different matrices, but that only makes sense going from tile to screen.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Why are your tiles given those coordinates? I mean sure you could map mouse coordinates to tiles that way, but why? \$\endgroup\$
    – jhocking
    Commented Oct 5, 2012 at 14:26
  • \$\begingroup\$ @jhocking it was probably easier for him to draw it like this. \$\endgroup\$ Commented Oct 15, 2012 at 22:17

5 Answers 5

3
\$\begingroup\$

Yes, it is possible, the easiest way would be to draw a 2d-array numerical representation of the tiles, where each tile 'pixel' in the array is coded with a serial tile number.

Otherwise

Here is the complete answer:

Assuming each tile is defined by this height and width properties:

TILE_WIDTH = xyz;
TILE_HEIGHT = abc;

And the game map looks like this:tile map We first make sure that anything on the green and red triangles is the '0' tile. Meaning a tile that does not exist.

We have as input the mouse (x, y)

offsetY = (mouseY - 0.5 * TILE_HEIGHT) % TILE_HEIGHT;
greenPixels = TILE_WIDTH * Math.abs(TILE_HEIGHT/2 - offsetY) / TILE_HIGHT;

offsetX = mouseX % TILE_WIDTH;
redPixels = TILE_HEIGHT * (TILE_WIDTH/2 - Math.abs(TILE_WIDTH/2 - offsetX)) / TILE_WIDTH;
if(mouseX < greenPixels || mouseY < redPixels) return (0,0);
else
{
    rowY = (int) Math.floor((mouseY -0.5 * TILE_HEIGHT) / TILE_HEIGHT);
    columnX = (int) Math.floor((mouseX * TILE_WIDTH) / TILE_WIDHT);
    if( (offsetX / TILE_WIDTH) + (offsetY / TILE_HEIGHT) < 1.0) return (columnX, 2 * rowY - 1);
    else
    {
        if(offsetX < TILE_WIDTH / 2) columnX --; // columnX = columnX - 1
        if(offsetY < TILE_HEIGHT / 2) return (columnX, 2 * rowY - 2);
        else return (columnX, 2 * rowY);
    }

}

This is the general pseudo code you need, there might be typos and you are welcome to ask questions and I will elaborate.

Generally what I did is build an imaginary square grid over the even rows of the tiles - ie the second row, the fourth row and such.

enter image description here

Then I check in which of this grid's squares the mouse is pointing (which is relatively simple since it is not jagged like the tiles.

Now I check the distance on x and y from the center of the square grid. If the pointer is not far enough from the center of the tile(which is also the center of the square in the grid) it is inside the tile.

Done like in this question:

Checking if an object is inside bounds of an isometric chunk

If not, if it is too high, it is one row above, if it's too low, it is one row below, if it is too much to the left, it is one column behind (cause the tiles on the un-even rows start a little later than the ones on the even rows) and if it is too much to the right than it is in the same jagged column as the even tile in the center.

\$\endgroup\$
4
  • \$\begingroup\$ There is a problem with calculating the x column position in this line, it will just follow the mouseX position with no modification I'm trying to fix this currently columnX = (int) Math.floor((mouseX * TILE_WIDTH) / TILE_WIDTH); \$\endgroup\$
    – Pent
    Commented Oct 16, 2012 at 2:18
  • \$\begingroup\$ I fixed the calculation by multiplying 0.25 to the tile width in offsetX and columnX \$\endgroup\$
    – Pent
    Commented Oct 16, 2012 at 4:47
  • \$\begingroup\$ I honestly did not really bug test, does it work now? \$\endgroup\$
    – AturSams
    Commented Oct 16, 2012 at 7:52
  • \$\begingroup\$ it "kind of" works, the mouse accuracy is poor, so I think there is still some tweaks to do \$\endgroup\$
    – Pent
    Commented Oct 16, 2012 at 17:24
1
\$\begingroup\$

Screen-space mouse coordinates to tile coordinates

The general dimetric projection matrices in my answer to the question linked above will get you the transformation from screen coordinates to tile coordinates. In particular, this matrix can be used for transformation of the screen coordinates to tile coordinates:

The values to input are:

  • w: half of the width of a single tile on screen
  • h: half of the height of a single tile on screen
  • fx: X-Position of the upper corner of the (0,0) tile in screen coordinates
  • fy: Y-Position of the upper corner of the (0,0) tile in screen coordinates

The mouse coordinates have to be extended by a constant value of 1 to be a 3-dimensional vector (xmouse, ymouse, 1) and multiplied by this matrix to arrive at the tile coordinates.

Tile coordinates to tile indices

Unfortunately if you apply them straight away to your image, you'll find that the actual tile indices aren't quite in line with their coordinates.

enter image description here

The somewhat thicker green lines in the picture are at coordinates x=0 and y=0; the others are spaced one unit of length apart. Remember that the y coordinate goes down as usual with computer graphics, so the tile indexed by (1,0) is in the tile with coordinates x=[1, 2) and y=[-1, 0).

Given the "normal" integer tile coordinates x and y (in blue on the picture above), you can calculate your tile indices xind and yind with the following formulas(1):

xind = floor(x - y + 1)/2)

yind = x + y


(1) In this context, the floor() function rounds down towards -∞.

\$\endgroup\$
2
  • \$\begingroup\$ Can you provide any further information about the derivation of the transformation matrix you have listed here? Where does it come from? Do you have a link or a book? \$\endgroup\$
    – Andrew
    Commented Mar 10, 2013 at 4:24
  • \$\begingroup\$ @AndrewAustin I linked the derivation at the start of my answer, but here is the link again. What you do is to calculate the inverse matrix of the tile-space to screen-space transformation matrix. Check any basic geometry book which has a chapter about projections for details. \$\endgroup\$ Commented Mar 13, 2013 at 11:48
0
\$\begingroup\$

If it's still not working, these tutorials may be of help:

http://www.gamedev.net/page/resources/_/technical/game-programming/isometric-n-hexagonal-maps-part-i-r747 , http://www.wildbunny.co.uk/blog/2011/03/27/isometric-coordinate-systems-the-modern-way/

\$\endgroup\$
1
  • \$\begingroup\$ The first one assumes a constant tile size (on screen), the second one assumes a sane coordinate system. Both are good articles, mind you. \$\endgroup\$ Commented Oct 17, 2012 at 9:03
0
\$\begingroup\$

As I saw on a game I did, it's easier to "move" your x/y axes, and handle row and cols as linear function : you can easy calculate a and b from y(x) = ax+b with tile position.

enter image description here

\$\endgroup\$
0
\$\begingroup\$

I'll preface my answer with this: I know this is an old question, but it's the first that came up when I was searching.
I had the same issue, and I looked at the solution presented here, but they deal with either a known offset or can be inaccurate with mouse coordinates. My solution is small and in a single function.

This is in Lua:

function global.XYtoIsoTile(x,y)
  local isoW,isoH = cGenericTile.getSize()
  local gridX,gridY = math.floor(x/isoW)+1,math.floor(y/isoH)+1
  --Get the coordinates in relation to center of grid area
  local pointX,pointY = (x%isoW)-(isoW/2),(y%isoH)-(isoH/2)

  if math.abs(pointY) > ((isoH/2) - (((isoH/2)*math.abs(pointX))/(isoW/2))) then
    if pointX >= 0 and pointY >= 0 then
      gridX,gridY = gridX,gridY+1/2
    elseif pointX >= 0 and pointY < 0 then
      gridX,gridY = gridX,gridY-1/2
    elseif pointX < 0 and pointY >= 0 then
      gridX,gridY = gridX-1,gridY+1/2
    else --if pointX < 0 and pointY < 0 then
      gridX,gridY = gridX-1,gridY-1/2
    end
  end
  return gridX,gridY
end

cGenericTile.getSize() just returns the height and width of my isometric tiles.
my gridX and gridY have the +1 to them because Lua tables start at 1, not 0, and my tiles are stored in a 2D table/array. In Lua, -- denotes a comment, rather than // or #

math.abs(pointY) > ((isoH/2) - (((isoH/2)*math.abs(pointX))/(isoW/2))) is a math function that, if graphed, forms an isometric tile. It checks if the |y| is above the line, and if so, it enters the if statement code, else skips over it all.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .