Search code examples
c#2dgrid-layoutisometricstaggeredgridlayout

Getting tile from coordinates on staggered isometric grid


I can't figure out the math. I have a staggered 2d isometric grid, got no problem into converting the grid cells into world coordinates, but now I can't figure out how to reverse the process.

Here is the CellToCoord method:

public static Vector2 CellToCoord(int x, int y) {
    return new Vector2() {
        x = x + ((y % 2) * 0.5f),
        y = y * -0.25f
    };
}

Pretty simple and display the grid exactly like i wanted it to be, I would like to get the tile from world coordinates now.

Edit:
Image, the world coordinates i get from the CellToCoord() method give me a world position which represent the center of the cell.


Solution

  • public static Point CoordToCell(Vector2 coord) {
        // Divide the cell coordinate the value again (or multiply by -4)
        // Then Math.Round() to eliminate any floating point inaccuracies
        var cellY = (int)Math.Round(coord.y / -0.25f);
    
        // Armed with the original Integer Y, we can undo the mutation of X (again, accounting for floating point inaccuracies)
        var cellX = (int)Math.Round(coord.x - ((cellY % 2) * 0.5f));
    
        return new Point() {
            x = cellX,
            y = cellY
        };
    }
    

    This should reverse the process. I've used Point as a generic container for an integer X and Y coordinate, you can replace this as appropriate.

    The biggest issue in my opinion is that the X mutation was originally based on the integer value of Y, from which you then stored a floating point value. I couldn't find a way to reverse this without the Math.Round()s to coerce the floating point results back into integer values reliably.

    Update Calculating the cell coordinates containing a given world coordinate:

    Looking at how your world coordinates are arranged, we can see that the centres of each cell are on the diagonal lines given by:

    • y = ( x - i ) / 2 ... or ... x - 2y = i
    • y = ( j - x ) / 2 ... or ... x + 2y = j

    where i and j are integers. This diagram shows this for the x - 2y = i diagonals.

    Diagram showing diagonal coordinates

    Using these, we can take the world coordinates of a given point, work out i and j, then round them to the nearest integer to get the centre of the cell as i and j.

    We can then use i and j to work out the x and y of the cell centre, and use the function I posted before to convert these back into cell coordinates.

    public static Point CoordToCell(Vector2 coord)
    {
        // Work out the diagonal i and j coordinates of the point.
        // i and j are in a diagonal coordinate system that allows us
        // to round them to get the centre of the cell.
        var i = Math.Round( coord.x - coord.y * 2 );
        var j = Math.Round( coord.x + coord.y * 2 );
    
        // With the i and j coordinates of the centre of the cell,
        // convert these back into the world coordinates of the centre
        var centreX = ( i + j ) / 2;
        var centreY = ( j - i ) / 4;
    
        // Now convert these centre world coordinates back into the
        // cell coordinates
        int cellY = (int)Math.Round(centreY * -4f);
        int cellX = (int)Math.Round(centreX - ((cellY % 2) * 0.5f));
    
        return new Point() {
            x = cellX,
            y = cellY
        };
    }
    

    I used the following coordinate points as test cases

    { x = 1.5f, y = -0.25f } => ( 1, 1 )        // Centre of 1, 1
    { x = 1.9f, y = -0.25f } => ( 1, 1 )        // Near the right of 1, 1
    { x = 1.5f, y = -0.374f } => ( 1, 1 )       // Near the bottom of 1, 1
    { x = 1.1f, y = -0.25f } => ( 1, 1 )        // Near the left of 1, 1
    { x = 1.5f, y = -0.126f } => ( 1, 1 )       // Near the top of 1, 1
    { x = 2.5f, y = -0.25f } => ( 2, 1 )        // Centre of 2, 1
    { x = 2f, y = -0.5f } => ( 2, 2 )           // Centre of 2, 2
    

    Hope this helps