0
\$\begingroup\$

I'm working on a 2D platformer game, and I'm having a lot of trouble with collision detection. I've looked trough some tutorials, questions asked here and Stackoverflow, but I guess I'm just too dumb to understand what's wrong with my code.

I've wanted to make simple bounding box style collisions and ability to determine on which side of the box the collision happens, but no matter what I do, I always get some weird glitches, like the player gets stuck on the wall or the jumping is jittery. You can test the game here: Platform engine test. Arrow keys move and z = run, x = jump, c = shoot. Try to jump into the first pit and slide on the wall.

Here's the collision detection code:

function checkCollisions(a, b)
{
    if  ((a.x > b.x + b.width) ||
        (a.x + a.width < b.x) ||
        (a.y > b.y + b.height) ||
        (a.y + a.height < b.y))
    {
        return false;
    }
    else
    {
        handleCollisions(a, b);
        return true;
    }
}

function handleCollisions(a, b)
{
    var a_top = a.y,
        a_bottom = a.y + a.height,
        a_left = a.x,
        a_right = a.x + a.width,
        b_top = b.y,
        b_bottom = b.y + b.height,
        b_left = b.x,
        b_right = b.x + b.width;

        if (a_bottom + a.vy > b_top && distanceBetween(a_bottom, b_top) + a.vy < distanceBetween(a_bottom, b_bottom))
        {
            a.topCollision = true;
            a.y = b.y - a.height + 2;
            a.vy = 0;
            a.canJump = true;
        }
        else if (a_top + a.vy < b_bottom && distanceBetween(a_top, b_bottom) + a.vy < distanceBetween(a_top, b_top))
        {
            a.bottomCollision = true;
            a.y = b.y + b.height;
            a.vy = 0;
        }

        else if (a_right + a.vx > b_left && distanceBetween(a_right, b_left) < distanceBetween(a_right, b_right))
        {
            a.rightCollision = true;
            a.x = b.x - a.width - 3;
            //a.vx = 0;
        }
        else if (a_left + a.vx < b_right && distanceBetween(a_left, b_right) < distanceBetween(a_left, b_left))
        {
            a.leftCollision = true;
            a.x = b.x + b.width + 3;
            //a.vx = 0;
        }

}
function distanceBetween(a, b)
{
    return Math.abs(b-a);
}
\$\endgroup\$
2
  • \$\begingroup\$ possible duplicate of How to determine collision direction between two rectangles? \$\endgroup\$
    – House
    Dec 8, 2012 at 18:06
  • \$\begingroup\$ This is a duplicate question. I'm sorry you weren't able to figure it out from the other answer, but making an additional question about how to implement it in your code is not how this site works. \$\endgroup\$
    – House
    Dec 8, 2012 at 18:07

1 Answer 1

4
\$\begingroup\$

You shouldn't be checking every block stored in collisionMap.

it is many times more faster only detecting blocks around the player by indexing the original tile map.

To check if you are standing on a tile:

  1. You get the player X,Y co-ordinate
  2. Add the player height to Y.
  3. Divide this X, Y by tile width, height
  4. Index the original map data and get the tile
  5. check if tile == "n" to determine if are standing on anything or not.

You do this twice, once for the bottom left hand side of the player and once on the bottom right hand side of the player.

Code is rough and some variables are just made up but names give clue to what is what:

 // returns true if there is a tile other than "n"
 function collided(x, y) {
     return game.currentLevel.map[y >> 5][x >> 5] != "n";
 }

 // Collision for standing...
 var bottomY = playerY + playerHeight;
 var isStanding = collided(playerX, bottomY) || collided(playerX + playerWidth, bottomY);

 // if isStanding is true then you've got to stop falling
 // The % 32 part ensures the player feet lands perfectly on the tile Y boundary
 if (isStanding) {
    playerY = (bottomY  % 32) - playerHeight;
    // set other stuff here such as canJump and velocityY = 0, e.t.c.
 }

You repeat this for left / right and top of the player.

Note: Your character height is higher than 32 pixels so you probably need to do 3 calls to collided, one for top, one for midway down, and one for bottom

Added more info:

 function collided(x, y) {
     return game.currentLevel.map[y >> 5][x >> 5] != "n";
 }

is the same as

 function collided(x, y) {
     return game.currentLevel.map[Math.floor(x / 32)][Math.floor(y / 32)] != "n";
 }

This >> means rotate the bits X number of times.

32 is a power of 2 number, i.e. it is a bit in binary, for this reason, rather than doing divide by 32, you can do >> 5 instead.

The advantage of doing this is the rotating bits is far faster at machine level than doing a divide and you also do not have to worry about rounding.

32 in binary is 00010000

So, if you 000010000 >> 5 Becomes 00000001

which is the same as 32 / 32 = 1

If you want to learn more, search for bitwise rotation

\$\endgroup\$
3
  • \$\begingroup\$ I got my collision detection to work perfectly now! Just out of curiosity, what do the [y >> 5] and [x >> 5] do? \$\endgroup\$
    – fnx
    Dec 8, 2012 at 21:37
  • \$\begingroup\$ Added note above \$\endgroup\$
    – John
    Dec 8, 2012 at 22:16
  • 2
    \$\begingroup\$ first, thx for your answer, but for the >> thing, it's not true : javascript uses only 64bit floating point numbers, so for the binary tricks, it converts to 32 bits, operate, then convert back to number (float). But since jit can optmize any expression in any way ( floor ( /32) == >> 5), so in fact all expressions have very close performances and this is more like a question of 'taste' (or readability). \$\endgroup\$ Dec 9, 2012 at 5:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .