# Tile-based collision detection

• 08-12-2011, 08:44 PM
Tile-based collision detection
Hello guys,

I've been working on a little game project of mine where I have a tile-based engine.
I've got a lot so far... But the collision detection is not working for me.
As far as I can see, it should work, but it just doesn't somehow.

I've got a demo here:

Move with WASD keys.

The edge function for collidable tiles:
Code:

```function get_edges(object):Array {         var r = Math.floor(Math.round(object.x+object.speedX+object.width*0.5)/WORLD.tileWidth);         var l = Math.floor(Math.round(object.x+object.speedX-object.width*0.5)/WORLD.tileWidth);         var b = Math.floor(Math.round(object.y+object.speedY+object.height*0.5)/WORLD.tileHeight);         var t = Math.floor(Math.round(object.y+object.speedY-object.height*0.5)/WORLD.tileHeight);                 var tr = currentLevel[t][r];         var tl = currentLevel[t][l];         var bl = currentLevel[b][l];         var br = currentLevel[b][r];                 var finalArray:Array = new Array();         finalArray["tr"] = tr;         finalArray["br"] = br;         finalArray["bl"] = bl;         finalArray["tl"] = tl;         finalArray["b"] = b;         finalArray["t"] = t;         finalArray["l"] = l;         finalArray["r"] = r;         return finalArray; }```
and here's the code for the actual collision detection and collision resolving:

Code:

```function CollisionObjectTile(object) {         var edges:Array = get_edges(object);         // GOING DOWN         if (object.speedY > 0)         {                 if (in_array(edges["br"],collidables) || in_array(edges["bl"],collidables))                 {                         object.speedY = 0;                         object.y = edges["b"] * WORLD.tileHeight - object.height * 0.5;                         object.onGround = true;                         if (! object.walking)                         {                                 object.speedX *=  WORLD.groundFriction;                         }                 }         }         // GOING UP         else if (object.speedY < 0)         {                 if (in_array(edges["tr"],collidables) || in_array(edges["tl"],collidables))                 {                         object.speedY = 0;                         object.y = (edges["t"]+1) * WORLD.tileHeight + object.height * 0.5;                         if (! object.walking)                         {                                 object.speedX *=  WORLD.groundFriction;                         }                 }         }         edges = get_edges(object);         // GOING LEFT         if (object.speedX < 0)         {                 if (in_array(edges["tl"],collidables) || in_array(edges["bl"],collidables))                 {                         object.x = (edges["l"]+1) * WORLD.tileWidth + object.width * 0.5;                         object.speedX = 0;                 }                         }         // GOING RIGHT         else if (object.speedX > 0)         {                 if (in_array(edges["tr"],collidables) || in_array(edges["br"],collidables))                 {                         object.x = edges["r"] * WORLD.tileWidth - object.width * 0.5;                         object.speedX = 0;                 }         } }```
I'd greatly appreciate it if someone could point me in the right direction or figure out what exactly I am doing wrong.

Thanks!
• 08-24-2011, 03:46 AM
Nialsh
You're checking the vertical cases before the horizontal cases, so they're winning every time.

Try making them weaker.

Code:

```function CollisionObjectTile(object) {         var BOUNCE = 0.5;         var edges:Array = get_edges(object);         // GOING DOWN         if (object.speedY > 0)         {                 if (in_array(edges["br"],collidables) || in_array(edges["bl"],collidables))                 {                         object.speedY = -Math.abs(object.speedY) * BOUNCE;                         object.y += object.speedY;                         object.onGround = true;                         if (! object.walking)                         {                                 object.speedX *=  WORLD.groundFriction;                         }                 }         }         // GOING UP         else if (object.speedY < 0)         {                 if (in_array(edges["tr"],collidables) || in_array(edges["tl"],collidables))                 {                         object.speedY = Math.abs(object.speedY) * BOUNCE;                         object.y += object.speedY;                         if (! object.walking)                         {                                 object.speedX *=  WORLD.groundFriction;                         }                 }         }         edges = get_edges(object);         // GOING LEFT         if (object.speedX < 0)         {                 if (in_array(edges["tl"],collidables) || in_array(edges["bl"],collidables))                 {                         object.speedX = Math.abs(object.speedX) * BOUNCE;                         object.x += object.speedX;                 }                         }         // GOING RIGHT         else if (object.speedX > 0)         {                 if (in_array(edges["tr"],collidables) || in_array(edges["br"],collidables))                 {                         object.speedX = -Math.abs(object.speedX) * BOUNCE;                         object.x += object.speedX;                 }         } }```
Setting the position directly can be a jarring thing. Unless you can get your conditions perfect, it looks more natural to manipulate the velocity.
• 08-24-2011, 11:18 AM
Thank you for your response Nialsh,

I always did it that way before, adjusting velocity instead of directly change the position of the object. But it's really not what I want for this project. A player is not supposed to bounce, he has to just stop when he reaches the ground.
Besides, the ground is like quicksand now, I still need to move the player above/below the tile directly through the x/y coordinates so it won't slowly "fall" inside the tile. And then we have another problem: the bounce will have the player "blink" for 1 vertical coordinate all the time.

I just don't understand why it's not working correctly. When you move left/right, object.speedX becomes greater or less than zero. Then I check for the edges top left or bottom left if object.speedX < 0 (because the player is moving to the left) and top right or bottom right if object.speedX > 0 (because player is moving to the right).

So what I noticed is that the bottom edges seem to be inside a tile, causing the player to move very abruptly to the edge of a tile horizontally. But this shouldn't happen since I first check the vertical collisions (top left, top right and bottom left, bottom right) - meaning I put the object above the tiles so it won't collide with the tiles at the bottom at all (for at least one frame).

But still even after I update the edges, they seem to be inside the tiles below.
What am I not seeing?

Edit: so I just changed this line:
Code:

`object.y = edges["b"] * WORLD.tileHeight - object.height * 0.5;`
to
Code:

`object.y = (edges["b"] * WORLD.tileHeight - object.height * 0.5)-1;`
and it fixed a lot of problems. But still, I have some issues with jumping against vertically stacked tiles. So for example, if you hold down left/right and want to jump on a tile, it notices that the top edge of the object sticks inside the tile (because of holding the left/right key, you are adding horizontal velocity to the object).

Any thoughts?
```function CollisionObjectTile(object) {         var edges:Array = get_edges(object);         yOverlap = 0;         // GOING DOWN         if (object.speedY > 0)         {                 if (in_array(edges["br"],collidables) || in_array(edges["bl"],collidables))                 {                         yOverlap = Math.abs(edges["b"] * WORLD.tileHeight - object.height * 0.5 - object.y);                 }         }         // GOING UP         else if (object.speedY < 0)         {                 if (in_array(edges["tr"],collidables) || in_array(edges["tl"],collidables))                 {                         yOverlap = Math.abs((edges["t"]+1) * WORLD.tileHeight + object.height * 0.5 - object.y);                 }         }         edges = get_edges(object);         xOverlap = 0;         // GOING LEFT         if (object.speedX < 0)         {                 if (in_array(edges["tl"],collidables) || in_array(edges["bl"],collidables))                 {                         xOverlap = Math.abs((edges["l"]+1) * WORLD.tileWidth + object.width * 0.5 - object.x);                 }                         }         // GOING RIGHT         else if (object.speedX > 0)         {                 if (in_array(edges["tr"],collidables) || in_array(edges["br"],collidables))                 {                         xOverlap = Math.abs(edges["r"] * WORLD.tileWidth - object.width * 0.5 - object.x);                 }         }         if (xOverlap < yOverlap && xOverlap != 0) { // handle X collision                         } else if (yOverlap != 0) { // handle Y collision                         } }```