Legend of Zelda Corner-cutting

Started by
3 comments, last by KellerHood 12 years, 6 months ago
I was playing The Legend of Zelda: Link's Awakening this afternoon, and I noticed, that if you are on the very edge of a tile, Link will cut the corner.
For example Say you have a tile in an L-shape like this:

[_

And the player is about here:

[_
P

When you push up on the control pad, Link will slide to the left to here:

[_
P

And then continue upwards to here:

P[_


I was looking at my tile collision code, and thought how the player gets 'caught' on the corners of tiles. I thought that this would be a good solution for that problem. I found that the cutoff point that determines if link will collide with the wall or slide is about 4-6 pixels. Using that information, how can I add this feature to my code?
I am using an altered version of a collision code from parallel realities. s is the player, and tm is the tilemap. tm->tiles is a two-dimensional numeric index for each tile, 0 is passable and everything else is solid.


void collide(sprite* s, tilemap* tm)
{
int TILE_SIZE = 16;
int i, x1, x2, y1, y2;

/* Test the horizontal movement first */

i = s->h > TILE_SIZE ? TILE_SIZE : s->h;

for (;;)
{
x1 = (s->x + s->xsp) / TILE_SIZE;
x2 = (s->x + s->xsp + s->w/* - 1*/) / TILE_SIZE;

y1 = (s->y) / TILE_SIZE;
y2 = (s->y + i - 1) / TILE_SIZE;

if (x1 >= 0 && x2 < tm->w && y1 >= 0 && y2 < tm->h)
{
if (s->xsp > 0)
{
/* Trying to move right */

if ((tm->tiles[y1][x2] != 0) || (tm->tiles[y2][x2]!=0))
{
/* Place the player as close to the solid tile as possible */

s->x = x2 * TILE_SIZE;

s->x -= s->w + 1;

s->x += 1;

s->xsp = 0;
}
}

else if (s->xsp < 0)
{
/* Trying to move left */

if ((tm->tiles[y1][x1] != 0) || (tm->tiles[y2][x1]!=0))
{
/* Place the player as close to the solid tile as possible */

s->x = (x1 + 1) * TILE_SIZE;

s->xsp = 0;
}
}
}

if (i == s->h)
{
break;
}

i += TILE_SIZE;

if (i > s->h)
{
i = s->h;
}
}

/* Now test the vertical movement */

i = s->w > TILE_SIZE ? TILE_SIZE : s->w;

for (;;)
{
x1 = (s->x) / TILE_SIZE;
x2 = (s->x + i - 1) / TILE_SIZE;

y1 = (s->y + s->ysp) / TILE_SIZE;
y2 = (s->y + s->ysp + s->h - 1) / TILE_SIZE;

if (x1 >= 0 && x2 < tm->w && y1 >= 0 && y2 < tm->h)
{
if (s->ysp > 0)
{
/* Trying to move down */

if ((tm->tiles[y2][x1] != 0) || (tm->tiles[y2][x2]!=0))
{
/* Place the player as close to the solid tile as possible */

s->y = y2 * TILE_SIZE;
s->y -= s->h;

s->ysp = 0;

//s->onGround = 1;
}
}

else if (s->ysp < 0)
{
/* Trying to move up */

if ((tm->tiles[y1][x1] != 0) || (tm->tiles[y1][x2]!=0))
{
/* Place the player as close to the solid tile as possible */

s->y = (y1 + 1) * TILE_SIZE;

s->ysp = 0;
}
}
}

if (i == s->w)
{
break;
}

i += TILE_SIZE;

if (i > s->w)
{
i = s->w;
}
}
}



How can I implement corner-cutting into this code? What is the logic/geometry behind it?
Advertisement
Try sketching it out on a piece of paper: if you're less than a certain fraction blocked by a colliding tile, move the player until he's all the way unobstructed, then allow upward movement as before.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Hey good catch! I've been noticing this in a ton of games lately- you can use this same feature to make hills, stairs, and other things work.

In my game engine I do something a bit weird- basically when an object moves, it's updated in this huge collision map which basically turns the screen into a bunch of tiles, and uses vectors to store which objects are in each tile. This makes it super easy to check for collisions because we can just check the tiles we currently reside in for collisions with the other objects that also reside currently in that tile. When an object then requests it's collisions- those collisions are sorted by distance from the object so the object only worries about those closest to it (This will probably change to something new a bit later, to handle odd sized tiles)

This makes the slipping method pretty easy to implement- and here's why: you don't have to worry about slipping past blocks you shouldn't slip past, because if there's a block in the way, it will be closer than the block you're slipping past, making that collision irrelevant.

So with that in mind, the actual implementation is quite simple. just check if the overlap on the opposite axis to your larger velocity is < xx pixels. If so, instead of doing a velocity axis collision, resolve the other axis. easy right? =]
The first thing that pops to my mind is to not treat the player as a square (or rectangle) but rather as a circle. By interpreting all the player collisions as a circle hitting a corner, if you implement the resolution properly he'll naturally slide around the object. It also creates nice collision resolution between the player and other characters and so on. I haven't played a classic Zelda in a while, but I'm fairly confident that's how the SNES version handles collisions.

Hey good catch! I've been noticing this in a ton of games lately- you can use this same feature to make hills, stairs, and other things work.

In my game engine I do something a bit weird- basically when an object moves, it's updated in this huge collision map which basically turns the screen into a bunch of tiles, and uses vectors to store which objects are in each tile. This makes it super easy to check for collisions because we can just check the tiles we currently reside in for collisions with the other objects that also reside currently in that tile. When an object then requests it's collisions- those collisions are sorted by distance from the object so the object only worries about those closest to it (This will probably change to something new a bit later, to handle odd sized tiles)

This makes the slipping method pretty easy to implement- and here's why: you don't have to worry about slipping past blocks you shouldn't slip past, because if there's a block in the way, it will be closer than the block you're slipping past, making that collision irrelevant.

So with that in mind, the actual implementation is quite simple. just check if the overlap on the opposite axis to your larger velocity is < xx pixels. If so, instead of doing a velocity axis collision, resolve the other axis. easy right? =]


That's a very nice idea, and I think I'll be using it for my next project. With this project, I'm trying to be as minimal as possible. I'm using plain C for the codes, and I want to be able to implement all my containers, etc. myself.


The first thing that pops to my mind is to not treat the player as a square (or rectangle) but rather as a circle. By interpreting all the player collisions as a circle hitting a corner, if you implement the resolution properly he'll naturally slide around the object. It also creates nice collision resolution between the player and other characters and so on. I haven't played a classic Zelda in a while, but I'm fairly confident that's how the SNES version handles collisions.


Interesting idea, though I think with a circle link would slide past everything, even head on tiles, because of the roundness.

Analyzing further, I noticed that link also seems to have a smaller bounding box than the tiles. The tiles are 16x16, his seems to be 10x10 or even 8x8. I noticed that he kind of overlaps everything he comes into contact with, which is what lead me to believe that.

Also, looking at my own collision code, and with a bit of thought, I don't think that corner cutting would be as difficult as I first thought it out to be. It's as simple as 'if his x (or y) coordinate is greater than the tiles x (or y) coordinate minus, say, 6, then add to or subtract from the opposite coordinate , making him slide along the tiles.

Thank you all for your input, you give some good ideas. But I think I've come up with a solution that will work for this particular project.


This topic is closed to new replies.

Advertisement