0
\$\begingroup\$

I'm working on a platform game and my collision code is to picky. I can't exactly do aabb(?) because the map is represented as metatiles. (23-24 16x16 tiles make up a 300ish pixel resolution.) My game thus far simply takes the player's x/y coords and divides them by 16 to check the current tile the player is in. Thus far the only thing I have managed is a very picky collision by simply checking the divided values against weather the tile in the map is solid or not and setting a bool to the player being blocked in that direction. It works, but not solid as the x and y velocity of the player changes so much the hit point of the collision does as well. For example going left at a small speed seems pixel perfect yet full speed results in the collision taking place 5 pixels before the actual collision.

Can anyone point me in the right direction on this?

\$\endgroup\$
6
  • \$\begingroup\$ If you really want good collisions in 2d use a velocity vector for you character and calculate if the player collide on the next Frame before moving it. Derivative + integration is a really good way of calculation the players next position using its velocity. Then you can look at the 9999 ways of calculating collision available from a simple google search \$\endgroup\$
    – ESD
    Apr 24, 2015 at 17:38
  • \$\begingroup\$ Game development will make you wish you had listened in those calculus classes :) \$\endgroup\$
    – ESD
    Apr 24, 2015 at 17:42
  • \$\begingroup\$ It's a simple platformer, I'm not really going for a next frame type system. I'm limiting myself to the basics so a simple explanation of the late 80s early 90s console style way of doing it would be dandy. \$\endgroup\$ Apr 24, 2015 at 17:46
  • \$\begingroup\$ You won't be able to get pixel perfect collision at high speed without checking collision one frame in advance. \$\endgroup\$
    – ESD
    Apr 24, 2015 at 17:50
  • \$\begingroup\$ The velocity / integration is advanced methods but checking collision one frame in advance is kind of mandatory \$\endgroup\$
    – ESD
    Apr 24, 2015 at 17:52

1 Answer 1

0
\$\begingroup\$

Here is what I usually use.

I have not checked it, but I hope it does not contain any mistakes.

Yes, it's a bit complex, but it gives very nice platformer physics.

int x = /*...*/, y = /*...*/; // Player coords.
int xv = 0, yv = 0; // Player horisontal and vertical speed.

/// Returns true if left key is being held.
bool LeftKeyHold() {/*...*/} 
/// Returns true if right key is being held.
bool RightKeyHold() {/*...*/}  
/// Returns true if jump key is was pressed at previous tick.
bool JumpKeyPressed() {/*...*/}  

/// This func tests if point at specific coords can be passed through.
bool IsSolidAt(int xpos, int ypos) {/*...*/} 
// Just divide xpos and ypos by tile size and check if tile at this position 
// is solid. If so, return true, else false. Don't forget to check if these 
// coordinates are in correct range, and if they are not, return false. 
// (Or true if you want solid map border.)

/// This func tests if player would be inside a wall at specific location
bool IsPosSolidForPlayer(int xpos, int ypos) 
{
    return IsSolidAt(xpos - 5, ypos - 5) ||
           IsSolidAt(xpos + 5, ypos - 5) ||
           IsSolidAt(xpos + 5, ypos + 5) ||
           IsSolidAt(xpos - 5, ypos + 5);
    // These are "collision points" of a character. They are relative to 
    // player's center. 
    // Usually they are just corner points of a character. In this case 
    // character is 11x11 pixels.
    // If character is larger than tile, you MUST add more points, look at 
    // image below.
    // Distance between two nearest points MUST be smaller than tile size. 
    // If it is larger, add more points. Toooo many points is bad because 
    // it can become slow.

    //     Character exapmples
    //              |
    //     +--------+-----+
    //     |              |
    //     V              |          'o's are collision points.
    //                    |
    // o'''o'''o          V
    // :       :
    // o   x   o        o'''o    #####
    // :       :        : x :    ##### <-- Tile
    // o...o...o        o...o    #####
    //                  
    //     ^              ^
    //     |              |
    //     |              +-- No extra points needed if character is small.
    //     +-- Notice the extra points at sides' centers. You would need 
    //         more if character is bigger.
}

/* ... */

while (true) // Main loop.
{
    /*...*/

    yv += 1;
    // Use another number if you want different gravity acceleration.
    // Remove completely if you don't need gravity.
    // Also modify xv if you want horisontal gravity too.
    // I assume you have fixed FPS. If not, you must add 'tick delta' 
    // multiplied by some number instead of fixed number.


    // Code below controls horisontal movement, remove if not needed.
    const int movementvel = 5; // Maximum movement velocity, pixels per tick.
    int hc = (RightKeyHold() - LeftKeyHold()) * 1; // Replace 1 with desired 
                  // player acceleration or leave it as 1 if you don't care.
    if (!hc)
    {
        // Speed at which player loses it's velocity if he is not 
        // controlled. You can replace 1 with any number or leave it as 1 
        // if you don't care.
        xv -= ((xv > 0) - (xv < 0)) * 1; 
    }
    else
    {
        xv += hc;
        if (xv > movementvel) xv = movementvel;
        else if (xv < -movementvel) xv = -movementvel;
    }

    // Code below controls jumps, remove if not needed.
    if (JumpKeyPressed() && IsPosSolidForPlayer(x, y+1))
        yv = -10; // Replace -10 with desired jump power. It should be negative.


    // Code below moves player depending on his velocity. Collision is 
    // pixel-perfect, character can't stuck inside a wall with it.
    const int maxvel = 10; // Maximum velocity, pixels per tick.
    if (xv)
    {
        int v = xv;
        if (v > 0)
        {
            if (v > maxvel) v = maxvel;
            while (v--)
            {
                if (!IsPosSolidForPlayer(x+1, y))
                    x += 1;
                else
                {
                    xv = 0;
                    break;
                }
            }
            if (v && IsPosSolidForPlayer(x+1, y)) xv = 0;
        }
        else
        {
            if (v < -maxvel) v = -maxvel;
            while (v++)
            {
                if (!IsPosSolidForPlayer(x-1, y))
                    x -= 1;
                else
                {
                    xv = 0;
                    break;
                }
            }
            if (v && IsPosSolidForPlayer(x-1, y)) xv = 0;
        }
    }
    if (yv)
    {
        int v = yv;
        if (v > 0)
        {
            if (v > maxvel) v = maxvel;
            while (v--)
            {
                if (!IsPosSolidForPlayer(x, y+1))
                    y += 1;
                else
                {
                    yv = 0;
                    break;
                }
            }
            if (v && IsPosSolidForPlayer(x, y+1)) yv = 0;
        }
        else
        {
            if (v < -maxvel) v = -maxvel;
            while (v++)
            {
                if (!IsPosSolidForPlayer(x, y-1))
                    y -= 1;
                else
                {
                    yv = 0;
                    break;
                }
            }
            if (v && IsPosSolidForPlayer(x, y-1)) yv = 0;
        }
    }

    /*...*/
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .