I am making a topdown rpg, similar to that.
My current code for checking simple, square tiles, is as follows:
void collide(ENTITY* e, int** tilemap, int map_width, int map_height)
{
/*
x1,y1____x2,y1
| |
| |
| |
x1,y2____x2,y2
*/
int i, x1, x2, y1, y2;
i = e->h > TILE_SIZE ? TILE_SIZE : e->h;
for (;;)
{
/*horizontal movement*/
x1 = (e->x + e->dirx) / TILE_SIZE;
x2 = (e->x + e->dirx + e->w /*- 1*/) / TILE_SIZE;
y1 = (e->y) / TILE_SIZE;
y2 = (e->y + i - 1) / TILE_SIZE;
if (x1 >= 0 && x2 < map_width && y1 >= 0 && y2 < map_height)
{
if (e->dirx > 0)
{
/* Trying to move right */
if ((tilemap[y1][x2] != T_BLANK) || (tilemap[y2][x2]!= T_BLANK))
{
if ((tilemap[y1][x2] == T_BLOCK) || (tilemap[y2][x2] == T_BLOCK))
{
/* Place the player as close to the solid tile as possible */
e->x = x2 * TILE_SIZE;
e->x -= e->w + 1;
e->dirx = 0;
}
if ((tilemap[y1][x2] == T_SLOPE_DR) || (tilemap[y2][x2] == T_SLOPE_DR))
{
/*?*/
}
if ((tilemap[y1][x2] == T_SLOPE_UR) || (tilemap[y2][x2] == T_SLOPE_UR))
{
/*?*/
}
}
}
else if (e->dirx < 0)
{
/* Trying to move left */
if ((tilemap[y1][x1] != T_BLANK) || (tilemap[y2][x1]!=T_BLANK))
{
if((tilemap[y1][x1] == T_BLOCK) || (tilemap[y2][x1] == T_BLOCK))
{
/* Place the player as close to the solid tile as possible */
e->x = (x1 + 1) * TILE_SIZE;
e->dirx = 0;
}
if ((tilemap[y1][x1] == T_SLOPE_DL) || (tilemap[y2][x1] == T_SLOPE_DL))
{
/*?*/
}
if ((tilemap[y1][x1] == T_SLOPE_UL) || (tilemap[y2][x1] == T_SLOPE_UL))
{
/*?*/
}
}
}
}
if (i == e->h)
{
break;
}
i += TILE_SIZE;
if (i > e->h)
{
i = e->h;
}
}
/* Now test the vertical movement */
i = e->w > TILE_SIZE ? TILE_SIZE : e->w;
for (;;)
{
/*vertical movement*/
x1 = (e->x) / TILE_SIZE;
x2 = (e->x + i) / TILE_SIZE;
y1 = (e->y + e->diry) / TILE_SIZE;
y2 = (e->y + e->diry + e->h) / TILE_SIZE;
if (x1 >= 0 && x2 < map_width && y1 >= 0 && y2 < map_height)
{
if (e->diry > 0)
{
/* Trying to move down */
if ((tilemap[y2][x1] != T_BLANK) || (tilemap[y2][x2]!=T_BLANK))
{
if (((tilemap[y2][x1] == T_BLOCK) || (tilemap[y2][x2] == T_BLOCK)))
{
/* Place the player as close to the solid tile as possible */
e->y = y2 * TILE_SIZE;
e->y -= e->h;
e->diry = 0;
}
if ((tilemap[y2][x1] == T_SLOPE_DR) || (tilemap[y2][x2] == T_SLOPE_DR))
{
if (e->dirx == 0)
e->dirx=-e->diry;
}
if ((tilemap[y2][x1] == T_SLOPE_DL) || (tilemap[y2][x2] == T_SLOPE_DL))
{
if (e->dirx == 0)
e->dirx=e->diry;
}
}
}
else if (e->diry < 0)
{
/* Trying to move up */
if ((tilemap[y1][x1] != T_BLANK) || (tilemap[y1][x2]!=T_BLANK))
{
if ((tilemap[y1][x1] == T_BLOCK) || (tilemap[y1][x2] == T_BLOCK))
{
/* Place the player as close to the solid tile as possible */
e->y = (y1 + 1) * TILE_SIZE;
e->diry = 0;
}
if ((tilemap[y1][x1] == T_SLOPE_UR) || (tilemap[y1][x2] == T_SLOPE_UR))
{
/*?*/
}
if ((tilemap[y1][x1] == T_SLOPE_UL) || (tilemap[y1][x2] == T_SLOPE_UL))
{
/*?*/
}
}
}
}
if (i == e->w)
{
break;
}
i += TILE_SIZE;
if (i > e->w)
{
i = e->w;
}
}
/* Now apply the movement */
e->x += e->dirx;
e->y += e->diry;
if (e->x < 0)
{
e->x = 0;
}
else if (e->x + e->w >= map_width * 32)
{
e->x = map_width * 32 - e->w - 1;
}
}
What this does is it takes the coordinates his four corners, and checks to see if there is a tile in front of the coordinates, based on his current speed vector (dirx and diry), and if that is true, then he will basically be 'snapped' to the grid. This is quite robust. The areas with no code and commented with a ? are when he 'collides' (or will collide) with some kind of slope tile, either Down-left, Down-right, Up-left, or Up-right.
I already know whether or not he will be in the bounding box of the slope, but how do I check whether he will collide with the hypotenuse? And how would I go about response, sliding him down the slope like in LTTP? They are only 45 degree slopes, but in 4 directions. Keep in mind this is only C, and the tiles are not data structures with coordinates, just a single number representing what type of tile it is. I had an idea to make his speed in the other direction equivalent to his speed in the direction he's going, to make him move diagonally whenever he came within the bounding box of the slope, but this technique had many problems. I need something more robust, but nothing too complicated.