Hi,
i've been trying to create a very simple 2D platformer game over the last few weeks. I'm basically trying to make a megaman clone. That's it.
No fancy physics or anything, just regular rectangle vs rectangle collision, non-rotated.
The problem is, i can't get the collision handling to work... i have rewritten the code a billion times and even though it should work, it doesn't and the player is just falling through the floor or not being able to walk on the floor, getting stuck in the floor, being teleported along tiles, being able to stand on walls and so on... For me this is something i ABSOLUTELY cannot wrap my head around
While looking for a solution i found 1000 pages describing how to detect collision and then just leaving it at that, as if that was the hard part...
I have found only a few pages that actually show some method on how it's supposed to be done, i tried implementing this one:
http://go.colorize.net/xna/2d_collision_response_xna/
But i can't get it to work... I also tried 100 different ways on my own:
1. Moving the player first, check if he's colliding, if so, move him out on the axis with smallest overlap, then check again and do the same for the other axis. Didn't work, i don't know why.
2. Moving the player first, check if he's colliding, if so, place him beside/on top of the block. Didn't work, i don't know why.
3. Check if player would collide in the next frame, given current position and velocity, if so, place him beside/on top of the block. Didn't work, and again, i can't figure out WHY...
And a few more that i already forgot, they are all kinda similiar except for one thing they have in common: THEY DON'T WORK
Another thing that happens with most of the techniques is, the player bounces on top of the tiles because i have to place him 1 pixel above the tile, or else he gets stuck inside. That means he will fall 1 pixel per frame and constanly bounce just a tiny bit. Looks very unprofessional and to me means the method is very poorly executed.
This is the most complicated thing i have ever come across and i need some help to understand what the best way to implementing something like a 2D Platformer like Megaman or Mario or whatever is.
I seriously doubt that i will ever in my life intuitively understand how it's supposed to work.
Moving player out of 1 tile on just 1 axis works, as soon as the other axis comes into play or more tiles, everything breaks and nothing works like it should.
Anyone out there who has real experience with this matter?
I'm not asking for how it theoretically should work, i know that, but when i try to write the code for it, it just DOESN'T work
Only moving the player when he's not colliding is ugly, because he will just float above the tiles or stop beside a wall with a gap inbetween.
The only thing left is brute force, move player pixel by pixel until he collides with something, but that's not very efficient and extremely bad code in my opinion, there has to be a better, more sophisticated way
Here's just one example of what i tried (left out the code for the other axis):
COLLISION_INFO ResolveCollisions(RECT* rect, std::vector<RECT>* obstacles, int dirX, int dirY)
{
COLLISION_INFO ci = {0,0};
int pushX = GetMinOverlapX(rect, obstacles);
int pushY = GetMinOverlapY(rect, obstacles);
if(abs(pushX) < abs(pushY)) // resolve smallest overlap first
{
ci.pushDirectionX = ResolveCollisionOnXAxis(rect, pushX, dirX);
pushY = GetMinOverlapY(rect, obstacles);
if(abs(pushY) > 0) // if other axis still overlapping
{
ci.pushDirectionY = ResolveCollisionOnYAxis(rect, pushY, dirY);
}
}
else
{
ci.pushDirectionY = ResolveCollisionOnYAxis(rect, pushY, dirY);
pushX = GetMinOverlapX(rect, obstacles);
if(abs(pushX) > 0) // if other axis still overlapping
{
pushX = GetMinOverlapX(rect, obstacles);
ci.pushDirectionX = ResolveCollisionOnXAxis(rect, pushX, dirX);
}
}
return ci;
}
int ResolveCollisionOnXAxis(RECT* rect, int pushX, int dirX)
{
if((dirX > 0 && pushX < 0) || (dirX < 0 && pushX > 0)) // don't push in the same direction as player is moving, or else bugs occur
{
if(pushX > 0)
{
rect->left += pushX+1; // 1 more to get out of the tile
return 1;
}
if(pushX < 0)
{
rect->left += pushX-1;
return -1;
}
}
return 0;
}
int GetMinOverlapX(RECT* rect, std::vector<RECT>* obstacles)
{
int minPushX = INT_MAX;
for(auto obstacle = obstacles->begin(); obstacle != obstacles->end(); obstacle++)
{
int dx1 = rect->left - obstacle->right;
int dx2 = rect->right - obstacle->left;
int thisPushX = 0;
if(abs(dx1) < abs(dx2))
{
// push player to the right
thisPushX = -dx1;
}
else
{
// push player to the left
thisPushX = -dx2;
}
if(abs(thisPushX) < abs(minPushX)) minPushX = thisPushX;
}
if(minPushX == INT_MAX) return 0; // if for whatever reason the vector was empty dont move
else return minPushX;
}