Sign in to follow this  
KellerHood

Topdown RPG slopes?

Recommended Posts

KellerHood    104
A Link to the past had basic tilemap collision, but it also had slopes, as shown in the picture.

[url="http://shadowcovenant.com/blog/wp-content/uploads/2010/10/link-to-the-past.jpg"][img]http://shadowcovenant.com/blog/wp-content/uploads/2010/10/link-to-the-past.jpg[/img][/url]

I am making a topdown rpg, similar to that.
My current code for checking simple, square tiles, is as follows:
[code]

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;
}
}
[/code]


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 [b]only [/b]45 degree slopes, but in 4 directions. Keep in mind this is only C, and the tiles are [b]not[/b] 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.

Share this post


Link to post
Share on other sites
ApochPiQ    23000
Filthy trick: don't try to make slopes.

Consider this hypothetical approach: There are tiles that are painted to [i]look[/i] like vertical surfaces. There are tiles which are marked obstructed to prevent you from "climbing" them. Then there are tiles which are marked passable, and painted to look like ramps, stairs, etc.

Now you get the visual effect of 2.5D terrain without writing a single line of code. It's all down to art and level design.

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='ApochPiQ' timestamp='1311633530' post='4840233']
Filthy trick: don't try to make slopes.

Consider this hypothetical approach: There are tiles that are painted to [i]look[/i] like vertical surfaces. There are tiles which are marked obstructed to prevent you from "climbing" them. Then there are tiles which are marked passable, and painted to look like ramps, stairs, etc.

Now you get the visual effect of 2.5D terrain without writing a single line of code. It's all down to art and level design.
[/quote]

No, I understand that trick and have been using it for other projects. But I want to know how to do strictly 2-dimensional slopes in a top-down environment.
Like in this image on the corners of the room:
[img]http://i.ytimg.com/vi/QDNAaxjM1gk/0.jpg[/img]

Share this post


Link to post
Share on other sites
Exorph    105
Just to clarify: By "slopes" you mean walls that run diagonally on a 2D surface? It's a bit confusing since the first image seems to contain ACTUAL slopes,as well as diagonal walls.

Can't you replace the bounding boxes of the diagonal walls with polygons or (if they are always on the outer part of the level) even just line segments?

Share this post


Link to post
Share on other sites
ApochPiQ    23000
[quote name='keelx' timestamp='1311637675' post='4840266']
No, I understand that trick and have been using it for other projects. But I want to know how to do strictly 2-dimensional slopes in a top-down environment.
Like in this image on the corners of the room:
[img]http://i.ytimg.com/vi/QDNAaxjM1gk/0.jpg[/img]

[/quote]



Your terminology is extremely confusing.

What you see in that room is not what I would call a "slope." There is no elevation change. All you are seeing is a rendering/artwork trick.

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='Exorph' timestamp='1311638269' post='4840272']
Just to clarify: By "slopes" you mean walls that run diagonally on a 2D surface? It's a bit confusing since the first image seems to contain ACTUAL slopes,as well as diagonal walls.

Can't you replace the bounding boxes of the diagonal walls with polygons or (if they are always on the outer part of the level) even just line segments?
[/quote]

I mean 2d walls going diagonally. Where in that image do you see anything else? The game contains vertical slopes, but not the image...
My game doesn't do a bounding box check, it does a grid-based check. Dividing the player's coordinates by the tile size, and checking the data grid if the player's location is passable (0 on the grid).
And in that game, they weren't a graphical trick either. The player would actually slide along them diagonally.

Share this post


Link to post
Share on other sites
[s]Ok, to clarify things up. What re you meaning? the corners? or the grass, that is in diagonal lines? or another thing?

please try to be more clear[/s]
[s]
[/s]
[s]
[/s]
ok, the walls cutting half a grid square.. now it's clear.

If you have a squre you can find the Hypotenuse, and have a square cut in half....

But it must have another way of doing this....

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='Mauricio Cinelli' timestamp='1311639547' post='4840285']
Ok, to clarify things up. What re you meaning? the corners? or the grass, that is in diagonal lines? or another thing?

please try to be more clear
[/quote]

I fail to see what isn't clear about this. It really isn't easy to explain in words, so I'm gonna fire up GIMP.
This is what I mean when I say 'slopes':
[attachment=4467:Slopes.png]

If Link comes into contact with a 'slope', and continues moving:
[attachment=4468:Start.png]
[attachment=4469:end.png]

He will slide along it, like in the above picture.
This is what I want. However, my code checks if he is coming into contact with the slope's bounding box. I want to know how to check if he has collided with the hypotenuse, and how to slide him along the slope like in LTTP. The tiles aren't objects, but numbers in a grid of data known in my code as tilemap[][].

Share this post


Link to post
Share on other sites
ApochPiQ    23000
Ah, you're talking about diagonal walls.


That's easy: you mark which corner is clear (only four possibilities: top left, top right, bottom left, bottom right). Based on that, you know the slope of the diagonal itself (it will be either +1 or -1) and the side of the diagonal (above or below) that should be passable. Then you write a total of five collision check cases:

[list][*]No corners are clear, i.e. the block is solid - use your existing code[*]Top left corner is clear[*]Top right corner is clear[*]Bottom left corner is clear[*]Bottom right corner is clear[/list]
For the solid block case, do as you already do. For diagonals, you just do an intersection between the line that defines the diagonal, and the player's bounding box. If they hit, move the player back "out" of the wall and slide them along it. If they don't overlap, allow the player to move freely.

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='ApochPiQ' timestamp='1311640880' post='4840299']
Ah, you're talking about diagonal walls.


That's easy: you mark which corner is clear (only four possibilities: top left, top right, bottom left, bottom right). Based on that, you know the slope of the diagonal itself (it will be either +1 or -1) and the side of the diagonal (above or below) that should be passable. Then you write a total of five collision check cases:

[list][*]No corners are clear, i.e. the block is solid - use your existing code[*]Top left corner is clear[*]Top right corner is clear[*]Bottom left corner is clear[*]Bottom right corner is clear[/list]
For the solid block case, do as you already do. For diagonals, you just do an intersection between the line that defines the diagonal, and the player's bounding box. If they hit, move the player back "out" of the wall and slide them along it. If they don't overlap, allow the player to move freely.
[/quote]


That's where my problem lies. The tiles have no coordinates. They are simply numbers in a 2d array of single integers.

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='ApochPiQ' timestamp='1311641337' post='4840305']
They still have coordinates.

The tile at [x][y] in your 2D array has the coordinates (x, y).

You can get the pixel coordinates by multiplying x and y by the width and height of a tile.
[/quote]
My code doesn't check the player's coordinates, but rather, the coordinates of his corners, x1 and y1 being his original x and y coordinates.
x1, y1_______x2,y1
| |
| |
| |
x1,y2_______x2,y2
These coordinates are offset by his speed, however. They are also converted into map coordinates.


He is inside the bounding box of a Down-right slope while he is moving right if this code returns true:
[code]

x1 = (x + xspeed) / TILE_SIZE;
x2 = (x + xspeed+ width) / TILE_SIZE;

y1 = y/ TILE_SIZE;
y2 = (y - 1) / TILE_SIZE;

if ((tilemap[y1][x2] == T_SLOPE_DR) || (tilemap[y2][x2] == T_SLOPE_DR))
{
/*inside bounding box*/
/*response here*/
/*original idea, didn't work well:
yspeed = xspeed;
*/
}
[/code]

What coordinates would I use? I suppose I could separate the or statement into to separate if-statements...

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='ApochPiQ' timestamp='1311642709' post='4840320']
You use pixel-space coordinates. Convert the tile indexes into pixel offsets, do your diagonal check, adjust the player's position, convert back to tile indices as needed.
[/quote]

Not what I meant.
I do a check on tilemap[y1][x2] and tilemap[y2][x2] , using a single or statement. Which one would I use? I could separate them into two if-statements, if that is the only option.

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='ApochPiQ' timestamp='1311648109' post='4840356']
Assuming your player is the same size as a map tile, he can overlap anywhere between 1 and 4 map tiles at any given time. For collision checking, you have to consider all of the tiles in the vicinity.
[/quote]

I get what you are saying. I do a check on both. However, you are partly incorrect (or simply inefficient) as you don't need to check for collision for all the tiles in the vicinity, just the ones he is moving towards, because if he is moving away from a tile anyway, need it be checked? I don't think so. But thank you for your input!

Share this post


Link to post
Share on other sites
KellerHood    104
[quote name='ApochPiQ' timestamp='1311652863' post='4840371']
How do you know if the player is moving away from a diagonal tile if you don't check the direction of the diagonal?
[/quote]

Because if I have a tile set up like this:
_
\ |

With a diagonal in the up-right corner, if he is moving down or left or both, he is moving away. My levels are structured that he would only ever intersect the hypotenuse of any diagonal tile.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this