Jump to content
  • Advertisement
Sign in to follow this  
Canvas

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

This topic is 1325 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.  

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites


@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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!