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

Author Topic: [SOLVED] SFML DIY RectangleShape Collision detection only partially working  (Read 1830 times)

0 Members and 1 Guest are viewing this topic.

noobie_low_level_dev

  • Newbie
  • *
  • Posts: 3
    • View Profile
Here is an attached video of my code in action.
https://imgur.com/a/qKK60EA

Below is the attached code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <math.h>
#include <vector>

int screenSizeX = 600;
int screenSizeY = 600;

bool inRange(float low, float hi, float test) {
    return (test > low && test < hi);
}

class genericSquare {
    public:
    float xSize{};
    float ySize{};
    float defaultX{};
    float defaultY{};
    float xDelta{};
    float yDelta{};
    bool moveable;
    sf::Vector2f position;
    sf::RectangleShape square;

    genericSquare(float xS, float yS, sf::Color color, float posX, float posY, float xD, float yD, bool moveabl) {
        xSize = xS;
        ySize = yS;
        defaultX = posX;
        defaultY = posY;
        xDelta = xD;
        yDelta = yD;
        moveable = moveabl;
        square.setSize(sf::Vector2f(xSize, ySize));
        square.setFillColor(color);
        square.setPosition(sf::Vector2f(posX, posY));
    }

    void playerInput();
};

std::vector<genericSquare> objectList;

int zz = 0;
bool checkCollisionAuxilary(float size, sf::Vector2f contains, char u) {
    for (int i = 1; i < objectList.size(); i++) {
        if (!objectList[i].moveable) {
            sf::FloatRect bounds = objectList[i].square.getGlobalBounds();
            for (zz = 0; zz < size+1; zz += size/size) {
                if (bounds.contains(contains)) {
                    std::cout << u << std::endl;
                    return true;
                }
        }
        }
    }

    return false;
}

bool checkCollision(genericSquare testee, char side, sf::Vector2f) {
    float xSize = testee.xSize;
    float ySize = testee.ySize;
    sf::Vector2f position = testee.square.getPosition();

    switch(side) {
        case &#39;l&#39;:
            return checkCollisionAuxilary(ySize, sf::Vector2f(position.x - 1, position.y + zz), &#39;l&#39;);
            break;
        case &#39;r&#39;:
            return checkCollisionAuxilary(ySize, sf::Vector2f(position.x + xSize + 1, position.y + zz), &#39;r&#39;);
            break;
        case &#39;u&#39;:
            return checkCollisionAuxilary(xSize, sf::Vector2f(position.x + zz, position.y - 1), &#39;u&#39;);
            break;
        case &#39;d&#39;:
            return checkCollisionAuxilary(xSize, sf::Vector2f(position.x + zz, position.y + ySize + 1), &#39;d&#39;);
            break;
        default:
            return false;
            break;
    }
}

void genericSquare::playerInput() {
    position = square.getPosition();
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) && position.x > 0 && !checkCollision(*this, &#39;l&#39;, position)) {
        square.move(sf::Vector2f(-xDelta, 0));
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) && position.x < screenSizeX - xSize && !checkCollision(*this, &#39;r&#39;, position)) {
        square.move(sf::Vector2f(xDelta, 0));
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) && position.y > 0 && !checkCollision(*this, &#39;u&#39;, position)) {
        square.move(sf::Vector2f(0, -yDelta));
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) && position.y < screenSizeY - ySize && !checkCollision(*this, &#39;d&#39;, position)) {
        square.move(sf::Vector2f(0, yDelta));
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::R)) {
        square.setPosition(defaultX, defaultY);
    }
}



int main() {
    sf::RenderWindow window(sf::VideoMode(screenSizeX, screenSizeY), "test");
    sf::Clock clock;
    float accumulator = 0;
    const float timestep = 1.0f / 60.0f; //60hz update frequency

    float xDelta = 8.f;
    float yDelta = 8.f;

    genericSquare player(40.f, 40.f, sf::Color::Red, 40.f, 40.f, xDelta, yDelta, true);
    objectList.push_back(player);

    genericSquare obstacle(200.f, 200.f, sf::Color::Yellow, 200.f, 200.f, 0.f, 0.f, false);
    objectList.push_back(obstacle);

    while (window.isOpen()) {
        sf::Event e;
        while (window.pollEvent(e)) {
            if (e.type == sf::Event::Closed)
                window.close(); }

        accumulator += clock.restart().asSeconds();
        while (accumulator >= timestep) {
            accumulator -= timestep;

            //Put time-reliant code here
            //Updates at nearly every 1/60th of a second

            player.playerInput();
        }

        window.clear();
   
        window.draw(obstacle.square);
        window.draw(player.square);    

        window.display();
    }
}
 

Basically, what it is doing when player.playerInput(); is called, is checking every pixel above the player when it is trying to go up, and if any object in
vector<genericSquare> objectList;
bounds contains the pixels above the player, it won't do up, and likewise for the 3 other directions. As you can see in the video, it only works when the player is fully contacting the yellow obstacle, which is weird hence it checks every pixel around the player, and if even one is in the globalBounds of the obstacle, it shouldn't move.

Would anyone mind helping me?

SOLVED:
Basically I tried to experiment with the checkCollisionAuxillary so I wouldn't have to copy and paste the same for loop in the checkCollision function, but that was the reason it was messing up. Probably something to do with the goofy zz variable setup.
« Last Edit: April 22, 2024, 02:44:15 am by noobie_low_level_dev »

noobie_low_level_dev

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: SFML DIY RectangleShape Collision detection only partially working
« Reply #1 on: April 21, 2024, 07:22:58 pm »
Seems like the formatting has been a little messed up in the switch() statement in bool checkCollision(), the
&#38;#39;l&#38;#39;)
should be just a character, going in order of 'l', 'r', 'u', 'd'.

noobie_low_level_dev

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: SFML DIY RectangleShape Collision detection only partially working
« Reply #2 on: April 21, 2024, 07:43:32 pm »
I just realized, this is in the description of the contains() function:
Check if a point is inside the rectangle&#39;s area This check is non-inclusive. If the point lies on the edge of the rectangle, this function will return false.

Could this be related to the problem, even though my collisions work in some cases perfectly?

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
To check if two (non-rotated) rectangles are intersecting, you can use Rect's intersects function (SFML 2 only; for SFML 3, you'll need the findIntersection function).

For example:
const sf::Vector2f rect1TopLeft{ 100.f, 100.f };
const sf::Vector2f rect1Size{ 200, 200.f };
const sf::Vector2f rect2TopLeft{ 200.f, 200.f };
const sf::Vector2f rect2Size{ 200, 200.f };
const sf::FloatRect rect1{ rect1TopLeft, rect1Size };
const sf::FloatRect rect2{ rect2TopLeft, rect2Size };
bool isRect1CollidingWithRect2{ rect1.intersects(rect2) };

It's a pretty simple function and you can do something similar yourself by comparing the edges: the right side of the leftmost rectangle must be past (more to the right than) the left side of the rightmost rectangle and/or the bottom side of the topmost rectangle must be past (lower than) the top side of the bottommost rectangle.

If you'd like to use rotated rectangles, there is a short simple function that can do that for you too:
https://github.com/SFML/SFML/wiki/Source%3A-Rectangular-Boundary-Collision
Obviously, it works on non-rotated rectangles too!
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*