3
votes

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).

1
It's very difficult to make suggestions that don't involve you completely rewriting things, without having a little more context. What engine are you using, if any. GL? DX? Or is rendering system abstracted. Where are the draw calls for the tiles, how are the vertices defined. Normally you'd want to do "picking" of some sort. But again it's very difficult to just start throwing out ideas without knowing how these things are defined and drawn.user562566
For example, you could split your tiles into two objects. Two fanned triangles for the diamond top shape and two more for each "side" of the tile. This would greatly simplify pixel perfect collision detection and such things.user562566
I'm using a DX based API, I've been building a game engine around this. The API is a proprietary piece provided by a lecturer so I'm unable to provide much more documentation. We were initially recommended to attempt a 2D shooter game but I want to create something a little more unique, hence the isometric attempts being built with an API that is unfortunately 2D-centricWearyWanderer
I've posted an answer that details what I believe to be your simplest and fastest-to-implement solution. Box2D is extremely well documented and widely used, so there is no shortage of code samples and answers to all the questions I'm sure you're bound to ask using it.user562566

1 Answers

3
votes

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.