### SFML Collision Question

So I asked this on the SFML Forums and the only response i got so far was that I could try to use Box2D, which would be nice but im not sure how to set it up right now and I want to learn how to do it with SFML just to learn, I'm not sure if anyne here knows SFML or not but the general basics of collision detection should still be relevant even if you dont. I was following a tutorial on youtube and this code was from me following along, I was wondering if all the code under General Collision can be simplified at all? Everything else in this code is pretty easy and stuff i could have done by myself, but the collision for the red box really threw me for a loop. The guy in the video admitted it probably isn't the best way but its really hard to understand and read, but i have to learn how to do it. any help would be greatly appreciated.

Heres the post on the SFML Site as well: https://en.sfml-dev.org/forums/index.php?topic=28558.0

 ``123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177`` ``````#include #include #include #include using std::cout; using std::vector; int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "SFML works!"); window.setFramerateLimit(60); float dt; sf::Clock dtClock; const float gridSize = 50.0f; //Player const float movementSpeed = 100.f; sf::Vector2f velocity; sf::RectangleShape player; player.setFillColor(sf::Color::Green); player.setSize(sf::Vector2f(gridSize, gridSize)); player.setPosition(sf::Vector2f(0, 0)); //Walls std::vector walls; sf::RectangleShape wall; wall.setFillColor(sf::Color::Red); wall.setSize(sf::Vector2f(gridSize, gridSize)); wall.setPosition(gridSize * 5, gridSize * 2); walls.push_back(wall); //Collision sf::FloatRect nextPos; while (window.isOpen()) { dt = dtClock.restart().asSeconds(); sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); //Prevents contents of window from being stretched if (event.type == sf::Event::Resized) { // update the view to the new size of the window sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); window.setView(sf::View(visibleArea)); } } //Player Movement velocity.x = 0.f; velocity.y = 0.f; if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) { velocity.y += -movementSpeed * dt; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) { velocity.x += -movementSpeed * dt; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) { velocity.y += movementSpeed * dt; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) { velocity.x += movementSpeed * dt; } //General Collision for (auto& wall : walls) { sf::FloatRect playerBounds = player.getGlobalBounds(); sf::FloatRect wallBounds = wall.getGlobalBounds(); nextPos = playerBounds; nextPos.left += velocity.x; nextPos.top += velocity.y; if (wallBounds.intersects(nextPos)) { //Bottom Collision if (playerBounds.top < wallBounds.top && playerBounds.top + playerBounds.height < wallBounds.top + wallBounds.height && playerBounds.left < wallBounds.left + wallBounds.width && playerBounds.left + playerBounds.width > wallBounds.left) { velocity.y = 0; player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height); } //Top Collision if (playerBounds.top > wallBounds.top && playerBounds.top + playerBounds.height > wallBounds.top + wallBounds.height && playerBounds.left < wallBounds.left + wallBounds.width && playerBounds.left + playerBounds.width > wallBounds.left) { velocity.y = 0; player.setPosition(playerBounds.left, wallBounds.top + wallBounds.height); } //Right Collision if (playerBounds.left < wallBounds.left && playerBounds.left + playerBounds.width < wallBounds.left + wallBounds.width && playerBounds.top < wallBounds.top + wallBounds.height && playerBounds.top + playerBounds.height > wallBounds.top) { velocity.x = 0; player.setPosition(wallBounds.left - playerBounds.width, playerBounds.top); } //Left Collision if (playerBounds.left > wallBounds.left && playerBounds.left + playerBounds.width > wallBounds.left + wallBounds.width && playerBounds.top < wallBounds.top + wallBounds.height && playerBounds.top + playerBounds.height > wallBounds.top) { velocity.x = 0; player.setPosition(wallBounds.left + wallBounds.width, playerBounds.top); } } } player.move(velocity); //Screen Collision //Left Collision if (player.getPosition().x < 0.0) { player.setPosition(0, player.getPosition().y); } //Top Collision if (player.getPosition().y < 0) { player.setPosition(player.getPosition().x, 0); } //Right Collision if (player.getPosition().x + player.getGlobalBounds().width > window.getSize().x) { player.setPosition(window.getSize().x - player.getGlobalBounds().width, player.getPosition().y); } //Bottom Collision //Right Collision if (player.getPosition().y + player.getGlobalBounds().height > window.getSize().y) { player.setPosition(player.getPosition().x, window.getSize().y - player.getGlobalBounds().height); } window.clear(); window.draw(player); for (auto& i : walls) { window.draw(i); } window.display(); } return 0; }``````
Last edited on
The actual collision detection happens on line 91. That's where you know it's a collision or not. The rest is just there to handle different kinds of collisions.

 ``123456789`` ``````//Bottom Collision if (playerBounds.top < wallBounds.top // is upper part of player higher up than the upper part of wall? && playerBounds.top + playerBounds.height < wallBounds.top + wallBounds.height // and is lower part of player higher up than lower part of wall? && playerBounds.left < wallBounds.left + wallBounds.width // and is left part of player to the left of right part of wall? && playerBounds.left + playerBounds.width > wallBounds.left) // and is right part of player to the right of the left part of wall? { velocity.y = 0; // stop player from moving in the y-direction (up or down) player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height); // place player so that bottom touches upper part of wall }``````

To me this seems a bit excessive. At this point you already know that the collision will take place so all you need to do is to determine whether this is a "bottom collision".

EDIT: After testing what I wrote below I realized it doesn't work. The problem is that all left and right collisions are also detected as either bottom or top collisions. I have not yet figured out why the original code did not run into this problem. I will update when I have figured it out.

EDIT2: Okay so I didn't pay enough attention to the fact that the code used the player position before the collision. The left/right checks ruled out collisions when the player was placed on the sides.

I think the second check should be enough:

 ``123456`` ``````//Bottom Collision if (playerBounds.top + playerBounds.height < wallBounds.top + wallBounds.height) // is lower part of player higher up than lower part of wall? { velocity.y = 0; player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height); }``````

EDIT3: It seems like you can at least remove the first check (note that the corresponding check for the other collisions is not always the first one)

 ``12345678`` ``````//Bottom Collision if (playerBounds.top + playerBounds.height < wallBounds.top + wallBounds.height // is lower part of player higher up than lower part of wall? && playerBounds.left < wallBounds.left + wallBounds.width // and is left part of player to the left of right part of wall? && playerBounds.left + playerBounds.width > wallBounds.left) // and is right part of player to the right of the left part of wall? { velocity.y = 0; player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height); }``````

Or you could just check the velocity?

 ``123456`` ``````//Bottom Collision if (velocity.y > 0) // is the player moving downwards? { velocity.y = 0; player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height); }``````
 ``12345678`` ``````//Bottom Collision if (velocity.y > 0 // is the player moving downwards? && playerBounds.left < wallBounds.left + wallBounds.width // and is left part of player to the left of right part of wall? && playerBounds.left + playerBounds.width > wallBounds.left) // and is right part of player to the right of the left part of wall? { velocity.y = 0; player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height); }``````

EDIT4: Checking the velocity this way has a slight problem that the player can get partially inside the wall if the collision happens on the corner of the wall. This will hardly be noticeable under normal circumstances (when dt is small). The original (with and without one check removed) also doesn't handle corner collisions perfectly but at least the player will be moved out of the wall on the next game loop iteration.

But don't take my word for it. You need to think these things through and test them.

These things have nothing to do with SFML so don't be afraid of looking at resources about collision detection/handing elsewhere.

Sorry for the confusion! I had forgot how tricky it can be to get collisions to work correctly.
Last edited on
Topic archived. No new replies allowed.