Search code examples
actionscript-32disometriccartesian

2d to Isometric formula


tileWidth is 64 and tileHeight is 32.

This is my map array:

var map:Array=
    [
        [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
        [2,3,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2],
        [2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2],
        [2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2],
        [2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
    ];

Here is how theyre being placed:

//place tiles
        for (var i:Number = 0; i < map.length; i++)
        {
            for (var j:Number = 0; j < map[i].length; j++)
            {
                placeTile(map[i][j],i,j);
            }
        }

//place the tile based on coordinates
    private function placeTile(id:uint,i:uint,j:uint)
    {
        var pos:Point=new Point();
        pos.x = i;
        pos.y = j;
        pos = twoDToIso(pos);

        tile = new MovieClip(atlas.getTextures(getTile(id)),20);
        tile.x = pos.x ;
        //tile.y = pos.y + (map.length/2)*tileHeight - tileHeight*4 ;//used to center
        tile.y = pos.y;
        tile.addEventListener(TouchEvent.TOUCH, mapDown);
        mapContainer.addChild(tile);
    }

This is the function used to plot out the 2d points to isometric:

//CONVERT POINT TO ISOMETRIC
    private function twoDToIso(pt:Point):Point
    {
        trace(" ");
        trace("twoDtoIso:");
        trace("2d: " + pt);
        var tempPt:Point = new Point(0,0);

        tempPt.x = (pt.x * tileWidth / 2) + (pt.y * tileWidth / 2);
        tempPt.y =  (pt.y * tileHeight / 2) - (pt.x * tileHeight / 2);


        trace("iso: " + tempPt);
        return (tempPt);
    }

What formula can I use to revert the coordinates I receive when clicking a tile, back to 2d / access to the map array?


Solution

  • The transformation from screen coordinates back to isometric is the inverse of your own twoDToIso: for every 2 pixels across the x axis, you subtract 1 from the y axis, so for the reverse you do the same for pixels across the y axis. (Hope that's as clear as I think it is.)

    However, the signs of the calculations depend on how your map is set up: with the x axis going diagonally up to the right, or down to the right (and, possibly, the same for y). I did my testing with a diagonal up-map, with y counting up to the lower right:

                 +x
              ..
           2,0   ..
       1,0   2,1    ..
    0,0   1,1   2,2    ..
       0,1   1,2   ..
          0,2   ..
             ..+y
    

    If your map is drawn differently, swap the signs for the x,y calculations (and possibly for the constant 32 as well).

    The conversion from mouse coordinates mx,my to isometric coordinates tilex,tiley is the following:

    int tilex = mx - 2*my + 32;
    int tiley = mx + 2*my - 32;
    
    tilex /= 64;
    tiley /= 64;
    
    drawcursor (tilex, tiley);
    

    and is pixel-perfect for tiles of 128x64:

    isometric cursor movement

    Moving the mouse cursor only one pixel lower will select the blank space to the right and below the blue selected tile.

    There is a small oddity here: the black space in the lower half of my image is "negative x", and when entering that, the cursor needs a further adjustment of +1 for the isometric x position. This is because in C[*] by default an integer is rounded "towards zero". Using floats and floor made it work; but in general, you should strive to use integers for frequent calculations such as these. (Also, I don't think that a negative x or y position would be valid in regular tile maps.)

    [*] I wrote my test program with C and SDL.

    If you want to be able to select individual pixels inside each tile (for instance, if you want to select a certain quarter of a tile), you can use the fractional part of tilex and tiley:

    innerx = tilex & 63;
    innery = tiley & 63;
    

    before doing the division by 64.