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);
}
Answer
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:
- You get the player X,Y co-ordinate
- Add the player height to Y.
- Divide this X, Y by tile width, height
- Index the original map data and get the tile
- 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
No comments:
Post a Comment