Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

2D Platformer Collision Handling

I am trying to create a 2D platformer (Mario-type) game and I am some having some issues with handling collisions properly. I am writing this game in C++, using SDL for input, image loading, font loading, etcetera. I am also using OpenGL via the FreeGLUT library in conjunction with SDL to display graphics.

My method of collision detection is AABB (Axis-Aligned Bounding Box), which is really all I need to start with. What I need is an easy way to both detect which side the collision occurred on and handle the collisions properly. So, basically, if the player collides with the top of the platform, reposition him to the top; if there is a collision to the sides, reposition the player back to the side of the object; if there is a collision to the bottom, reposition the player under the platform.

I have tried many different ways of doing this, such as trying to find the penetration depth and repositioning the player backwards by the penetration depth. Sadly, nothing I've tried seems to work correctly. Player movement ends up being very glitchy and repositions the player when I don't want it to. Part of the reason is probably because I feel like this is something so simple but I'm over-thinking it.

If anyone thinks they can help, please take a look at the code below and help me try to improve on this if you can. I would like to refrain from using a library to handle this (as I want to learn on my own) or the something like the SAT (Separating Axis Theorem) if at all possible. Thank you in advance for your help!

void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
    {
        de2dObj ballPrev;
        ballPrev.coords[0] = ball.coords[0];
        ballPrev.coords[1] = ball.coords[1];
        ballPrev.coords[2] = ball.coords[2];
        ballPrev.coords[3] = ball.coords[3];
        ballPrev.coords[0] -= ball.xspeed;
        ballPrev.coords[1] -= ball.yspeed;
        ballPrev.coords[2] -= ball.xspeed;
        ballPrev.coords[3] -= ball.yspeed;

        int up = 0;
        int left = 0;
        int right = 0;
        int down = 0;

        if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
        {
            left = 1;
        }

        if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
        {
            right = 1;
        }
        if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
        {
            up = 1;
        }
        if(block[i].coords[1] < ballPrev.coords[1] && ballPrev.coords[1] < block[i].coords[3] && block[i].coords[3] < ballPrev.coords[3])
        {
            down = 1;
        }

        cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;

        if (left == 1)
        {
            ball.coords[0] = block[i].coords[0] - 18.0f;
            ball.coords[2] = block[i].coords[0] - 2.0f;
        }
        else if (right == 1)
        {
            ball.coords[0] = block[i].coords[2] + 2.0f;
            ball.coords[2] = block[i].coords[2] + 18.0f;
        }
        else if (down == 1)
        {
            ball.coords[1] = block[i].coords[3] + 4.0f;
            ball.coords[3] = block[i].coords[3] + 20.0f;
        }
        else if (up == 1)
        {
            ball.yspeed = 0.0f;
            ball.gravity = 0.0f;
            ball.coords[1] = block[i].coords[1] - 17.0f;
            ball.coords[3] = block[i].coords[1] - 1.0f;
        }
    }
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
    {
        ball.gravity = -0.5f;
    }
}
}

To explain what some of this code means:

The blocks variable is basically an integer that is storing the amount of blocks, or platforms. I am checking all of the blocks using a for loop, and the number that the loop is currently on is represented by integer i. The coordinate system might seem a little weird, so that's worth explaining. coords[0] represents the x position (left) of the object (where it starts on the x axis). coords[1] represents the y position (top) of the object (where it starts on the y axis). coords[2] represents the width of the object plus coords[0] (right). coords[3] represents the height of the object plus coords[1] (bottom). de2dCheckCollision performs an AABB collision detection. Up is negative y and down is positive y, as it is in most games.

Hopefully I have provided enough information for someone to help me successfully. If there is something I left out that might be crucial, let me know and I'll provide the necessary information. Finally, for anyone who can help, providing code would be very helpful and much appreciated.

Thank you again for your help!

Edit 2: I have updated my code with a new algorithm that checks where the ball was previously before collision. Corner cases work on that single platform correctly now, and when I have a wall of objects, I can slide against it correctly now. The only remaining problem is that there is a small jittering effect that happens when I am on the ground, where the ball is constantly going up and down as if it is being pulled by gravity and then the ball falls back into the object again.

Edit: Here is a URL to an image trying to show the kinds of problems I am having: http://img8.imageshack.us/img8/4603/collisionproblem.png

In case the explanation in the picture doesn't make too much sense, the ball cannot move left past the corner of an object unless I jump over it. However, the ball can move right, but it gets repositioned to the right of the object while moving, which is not needed. This creates a skipping movement essentially, where it appears as the the ball is skipping over half of the object or so when I move right. If this doesn't make sense, please ask me and I'll try to clarify more.

like image 565
defender-zone Avatar asked Mar 01 '11 01:03

defender-zone


1 Answers

One problem with your code is that you only detect situations like this:

ball

If the circle happens to be fully inside the block, you don't reposition at all. And that's a problem.

You're trying to think about your simulation as if it were continuous, but keep in mind it's discrete. In general, if you only look at the current state of the ball, you really cannot know which side it collided with. Look at these two possibilities: cases

The first solution that comes to mind is to look at the last position of the ball as well; more precisely, look at the delta vector. See if the delta vector intersects a wall. If it does, reposition in an axis-aligned direction towards the wall intersected by the delta vector.

Edit: When I said "delta vector", I forgot that you're moving a square and not a single point. So, if you just look at the delta vector of the top-left corner, that's not going to be enough because it may not detect that part of the ball entered a block. Instead, you can look at the delta vectors of all 4 corners.

like image 95
Stefan Monov Avatar answered Sep 19 '22 19:09

Stefan Monov