Advertisement

(SDL) Collision Detection Malfunction???

Started by September 01, 2009 04:14 PM
6 comments, last by 1Bit 13 years, 4 months ago
Okay, so I am using SDL and rectangular collision detection. Whenever the player collides with the wall/block, he stops on top if it. Gravity is implemented. I noticed an error though; whenever the player jumps from a high place or falls from very high up, the player actually doesnt collide with the block. The function detects a collision and the player stops, but it is moving so fast that there is a gap of like 5 pixels between it and the platform. When the player is only 32 pixels, this is a big deal. How can I make it so that whenever the player falls from any distance at any speed that it will actually collide directly with the platform, rather than stopping 5 pixels before it hits. Thanks in advance for any help. Here are the code-bits:

bool check_collision( SDL_Rect A, SDL_Rect B )
{
    //The sides of the rectangles
    int leftA, leftB;
    int rightA, rightB;
    int topA, topB;
    int bottomA, bottomB;

    //Calculate the sides of rect A
    leftA = A.x;
    rightA = A.x + A.w;
    topA = A.y;
    bottomA = A.y + A.h;
        
    //Calculate the sides of rect B
    leftB = B.x;
    rightB = B.x + B.w;
    topB = B.y;
    bottomB = B.y + B.h;
    
    //If any of the sides from A are outside of B
    if( bottomA < topB )
    {
        return false;
    }
    
    if( topA > bottomB )
    {
        return false;
    }
    
    if( rightA < leftB )
    {
        return false;
    }
    
    if( leftA > rightB )
    {
        return false;
    }
    
    //If none of the sides from A are outside B
    return true;
}
This is the collision detection function itself.

void Player::move()
{
    //Activate Gravity
    yVel++;
    
    inAir = true;

    //When terminal velocity is reached(in free fall)
    if(yVel > 15)
    {
        yVel = 15;
    }
    
    
    if(box.y > SCREEN_HEIGHT)
    {
         box.y = 0;
    }

    box.y += yVel;

    for(int i=0; i <= 7; i++)
    {
        if(check_collision(box, wallBox))
        {

            box.y -= yVel;
            yVel = 0;
            inAir = false;
        }
    }

    box.x += xVel;

    for(int i=0; i <= 7; i++)
    {
        if(check_collision(box, wallBox))
        {
            box.x -=xVel;
        }
    }
    
    if (box.x < -16 && xVel < 0)
    {
        box.x = SCREEN_WIDTH + playerClip[ 0 ].x - 8;
    }
    
    if (box.x > SCREEN_WIDTH - 16 && xVel > 0)
    {
        box.x = -16 + playerClip[ 0 ].x;
    }
}
This is the function to move the player and handle basic mechanics.
Rather than just undoing the movement when you detect a collision, you can set the player's position relative the wall/block manually, e.g.

if(check_collision(box, wallBox)){    box.y = wallBox.y - box.h;    yVel = 0;    inAir = false;}
Advertisement
The other way I have seen of doing it is to check for an impending collision before the player/object moves, and if there is a collision, move it until it is touching the object.
KratosDon, thanks a ton! That worked perfectly and was exactly what I was looking for.

I could have either changed my collision detection algorithm or I could have done simply what you said and changed the movement function.

There is a small bug where if I touch a block from anywhere other than the top then it moves it to the top, but that is easily fixable with some added detection variables and ifs.

Thanks again for the assistance,

-Spark1313
Okay, so i've been experimenting for a few days and still can't get rid of that small bug that I posted earlier.

this is the move function

//Player Moving Functionvoid Player::move(){    //Activate Gravity    yVel++;        inAir = true;    //When terminal velocity is reached(in free fall)    if(yVel > 15)    {        yVel = 15;    }            if(box.y > SCREEN_HEIGHT)    {         box.y = 0;    }    box.y += yVel;    for(int i=0; i <= 7; i++)    {        if(check_collision(box, wallBox))        {            if(box.y > wallBox.y - box.h)            {                             box.y = wallBox.y - box.h;                yVel = 0;                inAir = false;            }            else if(box.y - box.h < wallBox.y)            {                box.y = wallBox.y + box.h;                inAir = true;            }                    }    }    box.x += xVel;        if (box.x < -16 && xVel < 0)    {        box.x = SCREEN_WIDTH + playerClip[ 0 ].x - 8;    }        if (box.x > SCREEN_WIDTH - 16 && xVel > 0)    {        box.x = -16 + playerClip[ 0 ].x;    }}



I want to make it so that whenever the player hits its head on the bottom of a block, instead of warping to the top of the block, it would just repel back and continue falling.

The code-bit I have been trying is:

            else if(box.y - box.h < wallBox.y)            {                box.y = wallBox.y + box.h;                inAir = true;            }


and it is not working as intended.

Any ideas on how to do this? Thanks again in advance.
You could try something like the following:
oldPos = previous position of the boxnewPos = new position of the box, after movementif(checkCollision(newPos, wallBox)) { if(box at oldPos is completely above wallBox) {  hitTheWallFromAbove(); } else if(box at oldPos is completely below wallBox) {  hitTheWallFromBelow(); } else {  hitTheWallFromTheSide(); }}


Depending on what movement you allow, and how exact it needs to be, you might want to extend this for diagonal movement, which could potentially cause the box to still hit the wall from the side, even though it was previously above the wall. Whether it matters depends on your game.
Advertisement
So, the check_collision() is overriden. Thanks.
I really hate to grave dig, I really do but I just had to post here, this thread really helped me a lot and I want to help people that might view this and have the same issue.

First off thanks KratosDon you were really helpful, and as to andrern question, here is how I fixed it:


if (Xvel > 0)
{
Pbox.x = Wall.x - Pbox.w;
}
else if (Xvel < 0)
{
Pbox.x = Wall.x + Wall.w;
}


pretty simple huh? what happens is you check to see which direction you are moving if Xvel > 0 then you should be moving right so place your character close to the LEFT side of the Wall you are colliding with, and the opposite if moving your character left.

This of course should be inside your collision check and it is the same for the Yvel just change the coordinate and such.

Thanks again and sorry for grave digging.

This topic is closed to new replies.

Advertisement