Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Rectangle collision detection is not accurate  (Read 3203 times)

0 Members and 1 Guest are viewing this topic.

decoder

  • Newbie
  • *
  • Posts: 8
    • View Profile
Rectangle collision detection is not accurate
« on: June 04, 2018, 04:10:30 pm »
The problem is the rect is not actually entirely hitting the wall. As you can see in the image, there's a little gap between them, and the gap's size is random.

int main()
{
        sf::RenderWindow window(sf::VideoMode(1920, 1080), "SFML Test");

        float rectVel = 200.f;
        sf::RectangleShape rect({ 50, 50 });
        sf::Vector2f rectOffset;
        rect.setFillColor(sf::Color::Red);
        rect.setPosition(static_cast<sf::Vector2f>(window.getSize()) / 2.f);

        sf::RectangleShape wall({ 20, 70 });
        wall.setFillColor(sf::Color::Blue);
        wall.setPosition(300.f, 200.f);

        sf::Clock timer;

        while (window.isOpen())
        {
                sf::Time deltaTime = timer.restart();

                sf::Event ev;
                while (window.pollEvent(ev))
                {
                        switch (ev.type)
                        {
                                case sf::Event::Closed:
                                        window.close();
                                        break;
                        }
                }

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                {
                        rect.move(0.f, -rectVel * deltaTime.asSeconds());
                        if (wall.getGlobalBounds().intersects(rect.getGlobalBounds()))
                                rect.move(0.f, rectVel * deltaTime.asSeconds());
                }
                else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                {
                        rect.move(0.f, rectVel * deltaTime.asSeconds());
                        if (wall.getGlobalBounds().intersects(rect.getGlobalBounds()))
                                rect.move(0.f, -rectVel * deltaTime.asSeconds());
                }

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
                {
                        rect.move(-rectVel * deltaTime.asSeconds(), 0.f);
                        if (wall.getGlobalBounds().intersects(rect.getGlobalBounds()))
                                rect.move(rectVel * deltaTime.asSeconds(), 0.f);
                }
                else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
                {
                        rect.move(rectVel * deltaTime.asSeconds(), 0.f);
                        if (wall.getGlobalBounds().intersects(rect.getGlobalBounds()))
                                rect.move(-rectVel * deltaTime.asSeconds(), 0.f);
                }

                window.clear(sf::Color::White);
                window.draw(wall);
                window.draw(rect);

                window.display();
        }

        return 0;
}
« Last Edit: June 08, 2018, 02:38:08 am by decoder »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10988
    • View Profile
    • development blog
    • Email
Re: Sprite collision detection is not accurate
« Reply #1 on: June 04, 2018, 05:02:27 pm »
Because that's how you implemented your logic.

Move entity -vx * deltaTime in the x direction.
If there's a collision, move entity vx*deltaTime in the x direction.

There's no code that would put the boxes exactly next to each other. The code only guarantees that the distance will be between 0 and vx * deltaTime, but it can take any value in that range and will most likely never be 0. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

decoder

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Sprite collision detection is not accurate
« Reply #2 on: June 05, 2018, 02:28:36 am »
There's no code that would put the boxes exactly next to each other. The code only guarantees that the distance will be between 0 and vx * deltaTime, but it can take any value in that range and will most likely never be 0. ;)

Can you suggest me some code because I'm only new to SFML.

decoder

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Sprite collision detection is not accurate
« Reply #3 on: June 05, 2018, 06:17:46 am »
I've played around and ended up with a nice solution, but I ain't feelin good about that coz I've always been thinking that I'd only done a hack just to get the desired behavior.

The code below says that the rect should clamp to the wall's bounds if any collision happened.

// Where...
// Rect A: Moving body
// Rect B: Static body
bool checkCollision(const sf::RectangleShape& rectA,
                            const sf::RectangleShape& rectB)
{
        return rectB.getGlobalBounds().intersects(rectA.getGlobalBounds());
}

......


                if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                {
                        rect.move(0.f, -rectVel * deltaTime.asSeconds());
                        if (checkCollision(rect, wall))
                                rect.setPosition(rect.getPosition().x, wall.getPosition().y + wall.getSize().y);
                }
                else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                {
                        rect.move(0.f, rectVel * deltaTime.asSeconds());
                        if (checkCollision(rect, wall))
                                rect.setPosition(rect.getPosition().x, wall.getPosition().y - rect.getSize().y);
                }

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
                {
                        rect.move(-rectVel * deltaTime.asSeconds(), 0.f);
                        if (checkCollision(rect, wall))
                                rect.setPosition(wall.getPosition().x + wall.getSize().x, rect.getPosition().y);
                }
                else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
                {
                        rect.move(rectVel * deltaTime.asSeconds(), 0.f);
                        if (checkCollision(rect, wall))
                                rect.setPosition(wall.getPosition().x - rect.getSize().x, rect.getPosition().y);
                }

 

The multiple wall version
enum class Direction
{
        Up, Down, Left, Right
};

void clampBody(sf::RectangleShape& rectA,
                           const sf::RectangleShape& rectB,
                           Direction dir);

// Where...
// Rect A: Moving body
// Rect B: Static body
void checkCollision(sf::RectangleShape& rectA,
                                        const sf::RectangleShape& rectB,
                                        Direction dir)
{
        if (rectB.getGlobalBounds().intersects(rectA.getGlobalBounds()))
                clampBody(rectA, rectB, dir);
}

void checkCollision(sf::RectangleShape& rectA,
                                        const std::vector<sf::RectangleShape>& rects,
                                        Direction dir)
{
        for (const sf::RectangleShape& rect : rects)
                if (rect.getGlobalBounds().intersects(rectA.getGlobalBounds()))
                        clampBody(rectA, rect, dir);
}

// Also where...
// Rect A: Moving body
// Rect B: Static body
void clampBody(sf::RectangleShape& rectA,
                           const sf::RectangleShape& rectB,
                           Direction dir)
{
        switch (dir)
        {
                case Direction::Up:
                        // Snap to the bottom of rectB
                        rectA.setPosition(rectA.getPosition().x, rectB.getPosition().y + rectB.getSize().y);
                        break;

                case Direction::Down:
                        // Snap to the top of rectB
                        rectA.setPosition(rectA.getPosition().x, rectB.getPosition().y - rectA.getSize().y);
                        break;

                case Direction::Left:
                        // Snap to the right side of rectB
                        rectA.setPosition(rectB.getPosition().x + rectB.getSize().x, rectA.getPosition().y);
                        break;

                case Direction::Right:
                        // Snap to the left side if rectB
                        rectA.setPosition(rectB.getPosition().x - rectA.getSize().x, rectA.getPosition().y);
                        break;
        }
}

...............

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                {
                        rect.move(0.f, -rectVel * deltaTime.asSeconds());
                        checkCollision(rect, walls, Direction::Up);
                }
               
                else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                {
                        rect.move(0.f, rectVel * deltaTime.asSeconds());
                        checkCollision(rect, walls, Direction::Down);
                }

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
                {
                        rect.move(-rectVel * deltaTime.asSeconds(), 0.f);
                        checkCollision(rect, walls, Direction::Left);
                }
                else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
                {
                        rect.move(rectVel * deltaTime.asSeconds(), 0.f);
                        checkCollision(rect, walls, Direction::Right);
                }
 

I think there might be a better solution than mine.
« Last Edit: June 05, 2018, 10:19:30 am by decoder »