2
\$\begingroup\$

I am trying to create a simple platformer game with libgdx. I implemented a collision detection for the player's bounding box with the map's tiles using the Rectangle.overlaps method.

In response I am trying to find out at what edge the player hits the tile and then react accordingly.

Somehow it happens that when the player collides on the X axis, the response of a collision on the Y axes happens (player gets moved to the top of the tile).

I was looking into the SAT (Seperating Axis Theorem), but I don't feel like it's applicable here, since I am not trying to draw any line betwen the Rectangles. In fact, since Rectangle.overlaps() is used I am only reacting to a collision already happening.

Here the source:

private void resolveCollision() {
    setUpPlayerRec(allObjects.get(0));
    for (Rectangle r2 : tiles) {
        if (!playerRec.overlaps(r2)) {
            continue;
        } else {
            onCollisionWithTile(r2);
        }
    }
}

private void onCollisionWithTile(Rectangle r2) {
    Gdx.app.log("Collision Detected", "Player collided with tile");

    float diff;

    if (playerRec.x +playerRec.width < r2.x) {
        // LEFT EDGE OF TILE
        controlledPlayer.getPosition().x =(r2.x - playerRec.width);
        controlledPlayer.getVelocity().x = 0;
    } else if (playerRec.x > r2.x + r2.width) {
        // RIGHT EDGE OF TILE
        controlledPlayer.getPosition().x =(r2.x + r2.width);
        controlledPlayer.getVelocity().x = 0;
    }

    if (playerRec.y < r2.y) {
        // HIT TOP OF TILE
        diff = (playerRec.y + playerRec.height) - r2.y;
        controlledPlayer.getPosition().y -= Math.abs(diff);
        jumptime = MAXJUMP;
        controlledPlayer.setJumpState(JUMP_STATE.GROUNDED);
    } else if (playerRec.y < r2.y + r2.height) {
        // HIT BOTTOM OF TILE
        controlledPlayer.setJumpState(JUMP_STATE.GROUNDED);
        jumptime = 0;
        diff = playerRec.y - (r2.y + r2.height);
        controlledPlayer.getPosition().y = r2.y + r2.height;                
    }
}
\$\endgroup\$
  • \$\begingroup\$ I wrote a tutorial on how to do AABB collision response correctly here: mijyn.github.io/tutorial/2013/06/05/platformer-aabb-cr.html See if this can help :) \$\endgroup\$ – MiJyn Apr 30 '14 at 19:58
  • \$\begingroup\$ Thank you! Very nice tutorial, helped me alot and reminded me to do more research before getting crazy :D \$\endgroup\$ – blankman Apr 30 '14 at 22:40
  • \$\begingroup\$ @MiJyn You should flesh out the details and post it as an answer. \$\endgroup\$ – MichaelHouse Apr 30 '14 at 22:44
  • \$\begingroup\$ @blankman Glad it could help! \$\endgroup\$ – MiJyn May 1 '14 at 4:52
  • \$\begingroup\$ @Byte56 will do :D \$\endgroup\$ – MiJyn May 1 '14 at 4:53
5
\$\begingroup\$

I wrote a tutorial here that could help: http://mijyn.github.io/tutorial/2013/06/05/platformer-aabb-cr.html

But, as StackExchange doesn't exactly love people linking tutorials, I'll give a basic overview of what it says. I was facing the challenge of not copying and pasting the entire post, but still making this answer more than just a link. I think I solved this by simply giving the concepts here, and leaving the implementation in the link.

Here are the beautiful graphics I'm using to demonstrate:

||||||||
|      | <- Object 1
||||||||

--------
-      - <- Object 2
--------

Alright, so, to check collision between 2 objects, you have to check the overlap between both objects:

Startpos1  Endpos1
v          v
||||||||||||
|     -----|--------
|     -    |       -       In this case, endpos1 > startpos2 and endpos1 < endpos2
||||||-|||||       -        Therefore, there is a collision.
      --------------        We must also check vice versa
      ^            ^
      Startpos2    Endpos2

We must check this on both axises, because if not, a situation like this could come up:

||||||||||
|        |
||||||||||
                     If we only check the X axis,
     ----------       this will be detected as a collision.
     -        -
     ----------

To resolve, we must find the overlap, and subtract it from the axis:

      ++++++            Overlap = Endpos1 - Startpos2

Startpos1  Endpos1      For this to work,
v          v             we must make sure that Startpos1 < Startpos2
||||||||||||
|     -----|--------
|     -    |       -
||||||-|||||       -
      --------------
      ^            ^
      Startpos2    Endpos2

If your game is 1D, the entire collision detection and response process is trivial: Check if there is a collision, and if so, subtract the overlap. Done!

However, chances are, your game is 2D or 3D. So, the obvious solution is to simply check each axis for collision and subtract the overlap if applicable. Also, we must make sure that both axises are colliding

Slight problem with that though:

||||||||||||
|     -----|--------         Where do you think this will resolve?
|     -    |       -
||||||-|||||       -
      --------------

||||||||||||
|          |
|          |
||||||||||||
            --------------
            -            -   Most likely not what you wanted
            -            -
            --------------

So therefore, we must only resolve one axis if possible. We must resolve the shortest overlap. This will work for most situations. Except for this:

||||||||||||
|          | --------------  Frame 1
|||||||||||| -            -
             --------------

                    ||||||||||||
             -------|------    |
             -      ||||||||||||  Frame 2
             --------------

Which is the shortest overlap? Y. So it'll end up like this:

                    ||||||||||||
                    |          |
                    ||||||||||||
             --------------
             -            -
             --------------

Not what we want.

To fix this, we must find which axises the previous frame collided with, and resolve the collision for the other axis:

||||||||||||
|          | --------------  Collides with Y
|||||||||||| -            -   Therefore, we must resolve X in the next frame
             --------------

||||||||||||
|          |
||||||||||||
             --------------  Collides with none
             -            -   Therefore, we must resolve both in the next frame
             --------------

Now it should work perfectly, right? Not quite (whoever told you that AABB is easy was lying!! XD). What if this happens?

||||||||||||
|          | --------------  Frame 1
|||||||||||| -            -
             --------------

                         ||||||||||||
             ------------|-         |
             -           ||||||||||||
             --------------  Frame 2

It will resolve the X axis (awesome!), but which side?

                           ||||||||||||
             --------------|          |
             -            -||||||||||||
             --------------

Not what we want!

How do we fix this? Let's go back a bit:

      ++++++            Overlap = Endpos1 - Startpos2

Startpos1  Endpos1      For this to work,
v          v             we must make sure that Startpos1 < Startpos2
||||||||||||
|     -----|--------
|     -    |       -
||||||-|||||       -
      --------------
      ^            ^
      Startpos2    Endpos2

True, we do have to make sure that Startpos1 < Startpos2. However, we must use Startpos1 from the previous frame, not this one.

Alright, so, now we have a good collision response function that should work great!

However..... (you knew this was coming, didn't you? XD) that's not the end of the story. While it works great for single objects, we might run into problems in some situations with more than one object:

      ------
      -    -
      -    -
      ------   Frame 1
||||||======
|    |=    =
|    |=    =
||||||======

      ------
      -    -
      -    -
    ||||||--   Frame 2
    | ===|==
    | =  | =
    |||||| =
      ======

The bottom object (the one with =) knows to resolve the X axis. However, the top one thinks that it's best to resolve the Y axis, because it doesn't know which side to resolve (none of the axises were colliding in Frame 1).

To fix this, we must sort the objects such that objects that collide with either axis with Object 1 in Frame 1 (in this example, it's the = one) are placed in front of objects that don't collide with either axis (in this example, it's the - one).

I apologize if my language style or clarity becomes worse later on in the post, I'm writing this at midnight XD

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.