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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <vector>

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<sf::RectangleShape> 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.

1
2
3
4
5
6
7
8
9
//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:

1
2
3
4
5
6
//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)

1
2
3
4
5
6
7
8
//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?

1
2
3
4
5
6
//Bottom Collision
if (velocity.y > 0) // is the player moving downwards?
{
    velocity.y = 0;
    player.setPosition(playerBounds.left, wallBounds.top - playerBounds.height);
}
1
2
3
4
5
6
7
8
//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.