1
\$\begingroup\$

Im making a 2D tile based game with slopes, and I need help on the collision detection. This question is not about determining the vertical position of the player given the horizontal position when on a slope, but rather the structure of the code.

Here is my pseudocode for the collision detection:

void Player::handleTileCollisions()
    {
        int left = //find tile that's left of player
        int right = //find tile that's right of player
        int top = //find tile that's above player
        int bottom = //find tile that's below player

        for(int x = left; x <= right; x++)
        {
            for(int y = top; y <= bottom; y++)
            {
                switch(getTileType(x, y))
                {
                    case 1: //solid tile
                    {
                        //resolve collisions
                        break;
                    }
                    case 2: //sloped tile
                    {
                        //resolve collisions
                        break;
                    }
                    default: //air tile or whatever else
                        break;
                }
            }
        }
    }

When the player is on a sloped tile, he is actually inside the tile itself horizontally, that way the player doesn't look like he is floating. This creates a problem because when there is a sloped tile next to a solid square tile, the player can't move passed it because this algorithm resolves any collisions with the solid tile. Here is a gif showing this problem:

Collision Problem

So what is a good way to structure my code so that when the player is inside a sloped tile, solid tiles get ignored?

\$\endgroup\$
1
\$\begingroup\$

The most fail-proof method is to simply not do collision with the solid blocks "under" the slope. A fairly simple pass over all tiles can determine which edges are "outer" edges (a tile edge that is not butted against another solid edge) and "inner" edges (edges which correspond directly to an edge on the adjacent block).

In your example, the solid block has at least two inner edges: the top edge butts against the bottom edge of the slope above it, and the left edge butts against the right edge of the slope left of it.

You needn't ever perform collision detection against inner edges. It is impossible to "touch" an inner edge since an object would have to move through the adjacent solid block so clearly the inner edge can never collide with anything.

You can store these inner/outer edge flags as a bit mask simply left. 0x01 as Top, 0x02 as Right, 0x04 as Bottom, and 0x08 as Left works fine (you can either set them to 1 for outer or 1 for inner, whichever you prefer). When doing your detection, you can check the bitmask to see if the edge should be considered.

Note that having a solid debug line drawing facility helps with testing. I know in past student projects while in school we found it great help and even fun to be able to turn on the drawing of all outer edges and then turn off drawing of the tiles themselves, turning the world into an outline vector-like affair. Adding a little extra info for the normals of the edges also came in handy when doing more involved physics.

You can also do edge combination. For any two edges with the same normal sharing a vertex, you can combine them into a single longer edge. This allows you to continue using tiles for editing while using more geometric approaches for collision, which can also solve a few other potential problems, and can be easily combined with Box2D's edge-chain support if you want to combine your tiles with a full 2D physics engine.

\$\endgroup\$
  • \$\begingroup\$ So don't preform collisions detection on inner edges, but do on outer edges? This would still cause problems here is one scenario. i.imgur.com/DooHPOJ.png The player's Y position would jump up to the top of the square tile. I guess I could just mark that as an inner edge so nothing would happen? \$\endgroup\$ – ProgrammerGuy123 Nov 2 '13 at 21:31
  • \$\begingroup\$ If you only want the center point to be the bottom, only collide with the center point. You don't have to be a box if that's not the collision shape you want. You could use a hot-point kind of system where you have points on the sides that detect horizontal collisions and then points on the bottom and top for vertical collisions, and just place them where you find most appropriate. You could have the points on the horizontal edges 25% of the height away from the top/bottom and then the vertical points right in the middle of their edges. \$\endgroup\$ – Sean Middleditch Nov 2 '13 at 21:49
  • \$\begingroup\$ So... if I switched to a hot-point system then only the tile that the point is in would be considered for a collision? \$\endgroup\$ – ProgrammerGuy123 Nov 2 '13 at 22:20
  • \$\begingroup\$ That is what you just asked for. Either you collide in the center point only, like your illustration and problem indicates you want, or you collide on multiple points (possibly the whole edge, either on an AABB around the whole sprite or an AABB that is only around the "core" of the sprite). You could also use multiple hot-points per side. Real Games(tm) have done this exact technique. \$\endgroup\$ – Sean Middleditch Nov 3 '13 at 4:12

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.