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\$
  • \$\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\$ – Antoine Apr 24 '15 at 17:38
  • \$\begingroup\$ Game development will make you wish you had listened in those calculus classes :) \$\endgroup\$ – Antoine Apr 24 '15 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\$ – thatoneguy2 Apr 24 '15 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\$ – Antoine Apr 24 '15 at 17:50
  • \$\begingroup\$ The velocity / integration is advanced methods but checking collision one frame in advance is kind of mandatory \$\endgroup\$ – Antoine Apr 24 '15 at 17:52
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\$

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.