WearyWanderer
WearyWanderer

Reputation: 181

Isometric Collision - 'Diamond' shape detection

My project uses an isometric perspective for the time being I am showing the co-ordinates in grid-format above them for debugging. However, when it comes to collision/grid-locking of the player, I have an issue.

Due to the nature of sprite drawing, my maths is creating some issues with the 'triangular' corner empty areas of the textures. I think that the issue is something like below (blue is what I think is the way my tiles are being detected, whereas the red is how they ideally should be detected for accurate roaming movement on the tiles:

Reference Screenshot

As you can see, the boolean that checks the tile I am stood on (which takes the pixel central to the player's feet, the player will later be a car and take a pixel based on the direction of movement) is returning false and denying movement in several scenarios, as well as letting the player move in some places that shouldn't be allowed.

I think that it's because the cutoff areas of each texture are (I think) being considered part of the grid area, so when the player is in one of these corner areas it is not truly checking the correct tile, and so returning the wrong results.

The code I'm using for creating the grid is this:

int VisualComponent::TileConversion(Tile* tileToConvert, bool xOrY)
{
    int X = (tileToConvert->x - tileToConvert->y) * 64; //change 64 to TILE_WIDTH_HALF
    int Y = (tileToConvert->x + tileToConvert->y) * 25;

    /*int X = (tileToConvert->x * 128 / 2) + (tileToConvert->y * 128 / 2) + 100;
    int Y = (tileToConvert->y * 50 / 2) - (tileToConvert->x * 50 / 2) + 100;*/
if (xOrY)
{
    return X;
}
else
{
    return Y;
}

}

and the code for checking the player's movement is:

bool Clsentity::CheckMovementTile(int xpos, int ypos, ClsMapData* mapData) //check if the movement will end on a legitimate road tile UNOPTIMISED AS RUNS EVERY FRAME FOR EVERY TILE
{
    int x = xpos + 7; //get the center bottom pixel as this is more suitable than the first on an iso grid (more realistic 'foot' placement)
    int y = ypos + 45;

    int mapX = (x / 64 + y / 25) / 2; //64 is TILE-WIDTH HALF and 25 is TILE HEIGHT

    int mapY = (y / 25 - (x / 64)) / 2;

for (int i = 0; i < mapData->tilesList.size(); i++) //for each tile of the map
{
if (mapData->tilesList[i]->x == mapX && mapData->tilesList[i]->y == mapY) //if there is an existing tile that will be entered
{
if (mapData->tilesList[i]->movementTile)
{
    HAPI->DebugText(std::to_string(mapX) + " is the x and the y is " + std::to_string(mapY));
    return true;
}
}
}
return false;
}​

I'm a little stuck on progression until having this fixed in the game loop aspect of things. If anyone thinks they either know the issue from this or might be able to help it'd be great and I would appreciate it. For reference also, my tile textures are 128x64 pixels and the math behind drawing them to screen treats them as 128x50 (to cleanly link together).

Upvotes: 3

Views: 2668

Answers (1)

James
James

Reputation: 8586

Rather than writing specific routines for rendering and click mapping, seriously consider thinking of these as two views on the data, which can be transformed in terms of matrix transformations of a coordinate space. You can have two coordinate spaces - one is a nice rectangular grid that you use for positioning and logic. The other is the isometric view that you use for display and input.

If you're not familiar with linear algebra, it'll take a little bit to wrap your head around it, but once you do, it makes everything trivial.

So, how does that work? Your isometric view is merely a rotation of a bog standard grid view, right? Well, close. Isometric view also changes the dimensions if you're starting with a square grid. Anyhow: can we just do a simple coordinate transformation?

Logical coordinate system -> display system (e.g. for rendering)

Texture point => Rotate 45 degrees => Scale by sqrt(2) because a 45 degree rotation changes the dimension of the block by sqrt(1 * 1 + 1 * 1)

Display system -> logical coordinate system (e.g. for mapping clicks into logical space)

Click point => descale by sqrt(2) to unsquish => unrotate by 45 degrees

Why?

If you can do coordinate transformations, then you'd be dealing with a pretty bog-standard rectangular grid for everything else you write, which will make your any other logic MUCH simpler. Your calculations there won't involve computing angles or slopes. E.g. now your "can I move 'down'" logic is much simpler.

Let's say you have 64 x 64 tiles, for simplicity. Now transforming a screen space click to a logical tile is simply:

(int, int) whichTile(clickX, clickY) {
    logicalX, logicalY = transform(clickX, clickY)
    return (logicalX / 64, logicalY / 64)
}

You can do checks like see if x0,y0 and x1,y1 are on the same tile, in the logical space by someting as simple as:

bool isSameTile(x0, y0,  x1, y1) {
  return floor(x0/64) == floor(x1/64) && floor(y0/64) == floor(y1/64)
}

Everything gets much simpler once you define the transforms and work in the logical space.

http://en.wikipedia.org/wiki/Rotation_matrix

http://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation

http://www.alcove-games.com/advanced-tutorials/isometric-tile-picking/

If you don't want to deal with some matrix library, you can do the equivalent math pretty straightforwardly, but if you separate concerns of logic management from display / input through these transformations, I suspect you'll have a much easier time of it.

Upvotes: 3

Related Questions