SFML Bounding box but how to implment collision with side detection?

Started by
4 comments, last by BeerNutts 9 years, 5 months ago

Hello there GameDev community,

I am creating a small game project for myself where the player controls a ball which bounces, the player can move the ball left and right. Depending on what side the ball bounces from depends on how the ball should react, so if the ball bounces from its bottom it will bounce upwards, if from the left side it will bounce to the right and so on.

So let me explain some variables for my player, the player's width and height is 60 and its Y velocity may not be greater than 14,

I have a bunch of displayObjects (displayobject is just an object with a sf::Sprite, it is a custom class of course) which are checked to see if the player collides with them.

So first off here is a screenshot of my game so you understand it graphically.

wPYKzdj.gif

So first off I have a function that checks to see if the player is hitting any of the displayobjects, if the player is intersecting them the sf::IntRect is stored into a vector of sf::IntRects ready to be used later (They are stored because the player could hit multiple blocks)


void LevelState::CheckPlayerCollisions()
{
    std::vector<sf::IntRect> blocks;
    sf::IntRect playerRect(Player.XPos - 30, Player.YPos - 30, Player.Sprite.getTextureRect().width, Player.Sprite.getTextureRect().height);

    for (int i = 0; i < DisplayObjects.size(); i++)
    {
        sf::IntRect objectRect(DisplayObjects.at(i).XPos, DisplayObjects.at(i).YPos, DisplayObjects.at(i).Sprite.getTextureRect().width, DisplayObjects.at(i).Sprite.getTextureRect().height);

        if (objectRect.intersects(playerRect))
        {

            blocks.push_back(objectRect);
        }
    }

    if (blocks.size() != 0)
    {
        std::cout << "Collision !" << std::endl;
        CollisionSide collisionSide = _mathsService.Debug(blocks, playerRect);
        Player.HitObjectUpdate(14, collisionSide);
    }
}

Once all of the displayObjects have been checked I call my mathsService Debug function (the function is called debug as I am testing this collision detection, once fully working I will of course rename it), this checks the player against each sf::IntRect in the vector, it works out the centre of the player and the centre of each sf::IntRect in the vector and states which one the player was closest to and which side the player ball bounces from.


CollisionSide MathsService::Debug(std::vector<sf::IntRect> blocks, sf::IntRect player)
{
    int playerXCentre = player.left + (player.width / 2);
    int playerYCentre = player.top + (player.height / 2);

    int blockId = 0;
    int difference = 0;
    CollisionSide side = CollisionSide::None;

    for (int i = 0; i < blocks.size(); i++)
    {
        int blockXCentre = (blocks.at(i).left + (blocks.at(i).width / 2));
        int blockYCentre = (blocks.at(i).top + (blocks.at(i).height / 2));

        int xDifference = playerXCentre - blockXCentre;
        int yDifference = playerYCentre - blockYCentre;

        int positiveX = 0;
        int positiveY = 0;

        if (xDifference < 0)
        {
            positiveX = xDifference * -1;
        }
        else
        {
            positiveX = xDifference;
        }

        if (yDifference < 0)
        {
            positiveY = yDifference * -1;
        }
        else
        {
            positiveY = yDifference;
        }

        //Work out which side was hit
        if (positiveX > positiveY)
        {
            //It is left or right
            if (xDifference < 0)
            {
                side = CollisionSide::Left;
            }
            else
            {
                side = CollisionSide::Right;
            }
        }
        else
        {
            //It is top or bottom
            if (yDifference < 0)
            {
                side = CollisionSide::Bottom;
            }
            else
            {
                side = CollisionSide::Top;
            }
        }

        if (difference != 0)
        {
            int newDifference = positiveX + positiveY;

            if (newDifference < difference)
            {
                difference = newDifference;
                blockId = i;
            }
        }
        else
        {
            difference = xDifference + yDifference;
        }

    }

    return side;
}

Once it goes through the vector it will return a CollisionSide which can be Top, Right, Bottom or Left, depending on that result depends on what the player does, so then I call Player.HitObjectUpdate which then forces the ball to move correctly


void Player::HitObjectUpdate(int forceSet, CollisionSide side)
{
    if (side == CollisionSide::Top)
    {
        YVelocity = 0;
    }
    else if (side == CollisionSide::Left && XSpeed > 0)
    {
        XVelocity = XVelocity * -1;
        XSpeed = XSpeed * -1;
    }
    else if (side == CollisionSide::Right && XSpeed < 0)
    {
        XVelocity = XVelocity * -1;
        XSpeed = XSpeed * -1;
    }
    else if (side == CollisionSide::Bottom)
    {
        YVelocity = forceSet;
        XSpeed = XVelocity;
    }
}

Now this works fine for a few seconds or even minutes but now and then the ball will get stuck in a block and do weird movement then jump out or just go off the screen, I'm really confused on how to check for this collision. I don't want to use any existing 2D c++ physics engine. If anyone can give me a hand with this or know of a better way to work out collision detection please let me know.

Also you can view a video at http://tinypic.com/r/2u90aqe/8 to see the weird out come of the ball when it collides with a block and goes inside of it.

Advertisement

From looking at the video, I would guess that the ball is colliding with two blocks at the same time. It looks like you are checking the box on the right before the box under it.

I've had this problem before. You really need to test for as many collisions as possible, and then determine the collision point, and start with the collision closest to the player. This would have you checking the left box first. Currently you only check for one, then you move the player right through another box, which of course caused trouble.

I think, therefore I am. I think? - "George Carlin"
My Website: Indie Game Programming

My Twitter: https://twitter.com/indieprogram

My Book: http://amzn.com/1305076532

My suggestion for these kinds of questions is to move the objects in a single axis (say X-axis, so horizontal) and check collision. If collided, you know it hit from the side. Then move on the other axis, and check it's collision.

Yes, it double the number of collision checks, but it is very accurate w/respect to finding out which side has collided, and its easy to implement.

Gopd luck and have fun.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Just from the video, it appears that the errant collision takes place right at the intersection between two blocks and your code (TLDR) doesn't account for that possibility. Further, you're doing integer (rather than floating-point) calculations and may not properly detect collisions where differences are less than 0.5.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Sorry for the late reply guys, I went on Holiday for quite a few days :D,

@GlassKnife this may seem to be the problem let me investigate

@Beernutts So basically check the X axis then check the Y axis? But lets say my ball has just fallen inside of a block, wouldn't my ball now bounce up and the opposite X direction for no reason?

@Buckeye I have changed my sf::IntRect to sf::FloatRect and it seem much better however I am still getting some odd effects, but they are happening a lot frequently


@Beernutts So basically check the X axis then check the Y axis? But lets say my ball has just fallen inside of a block, wouldn't my ball now bounce up and the opposite X direction for no reason?

Move along the X axis, and check for collision. If there is a collision, then you know it happened moving X. Move the object back to the original spot, and inverse the X velocity.

Now move along the Y axis, and check for collision. If there is a collision, then you know it happened moving Y. Move the object back to the original spot, and inverse the Y velocity.

That handles hitting blocks from any side, and reacts properly.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

This topic is closed to new replies.

Advertisement