SFML collision help

Pages: 12
Ok so I am trying to set up a collision object with a player sprite and a red box sprite but I cant seem to get it working, i'm not really sure what exactly to do.

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
#include <iostream>
#include <math.h>
#include <SFML/Graphics.hpp>

using namespace std;

int main()
{
    sf::Vector2f position;
    sf::Vector2f velocity;
    float maxspeed = 4.0f;
    float accel = 1.5f;
    float decel = 0.01f;
    bool vSyncEnabled = true;

    //Setup Window and Framerate
    sf::RenderWindow window(sf::VideoMode(800, 600), "Sprite Movement");
    window.setFramerateLimit(60);
    window.setVerticalSyncEnabled(vSyncEnabled);

    //Load Texture
    sf::Texture character;
    if(!character.loadFromFile("Resources/Textures/triangle.png"))
    {
        cout << "Error loading resource '::Texture'" << endl;
    }
    else
    {
        cout << "Texture Loaded" << endl;
    }

    //Set Sprite for Character Object
    sf::Sprite characterSprite;
    characterSprite.setTexture(character);
    characterSprite.setOrigin(sf::Vector2f(0, 0));
    characterSprite.setPosition(400, 300);

    //Load Red Block Texture
    sf::Texture redBlock;
    if(!redBlock.loadFromFile("Resources/Textures/RedBlock.png"))
    {
        cout << "Error loading Red Block Texture" << endl;
    }
    else
    {
        cout << "Red block texture Loaded" << endl;
    }

    //Set Sprite for Red Block Object
    sf::Sprite redBlockSprite;
    redBlockSprite.setTexture(redBlock);
    redBlockSprite.setOrigin(0, 0);
    redBlockSprite.setPosition(200, 500);

    //Collision
    sf::FloatRect boundingBoxRBS = redBlockSprite.getGlobalBounds();
    sf::FloatRect boundingBoxPLS = characterSprite.getGlobalBounds();

    if(boundingBoxRBS.intersects(boundingBoxPLS))
    {
       if(position.x >= redBlockSprite.getGlobalBounds().width)
       {

       }
    }


    while(window.isOpen())
    {
        sf::Event event;

        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                {
                    window.close();
                }
                break;
                //Dont stretch the window contents when its resized
                case sf::Event::Resized:
                {
                    sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height);
                    window.setView(sf::View(visibleArea));
                }
                break;
            }
        }

        window.clear(sf::Color::White);

        window.draw(characterSprite);
        window.draw(redBlockSprite);

        //WASD Movement
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            velocity.x -= accel;
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            velocity.x += accel;
        }
        else
        {
            velocity.x *= decel;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        {
            velocity.y -= accel;
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
        {
            velocity.y += accel;
        }
        else
        {
            velocity.y *= decel;
        }

        //Make sure the sprite isn't going too fast
        if(velocity.x < -maxspeed) velocity.x = -maxspeed;
        if(velocity.x >  maxspeed) velocity.x =  maxspeed;
        if(velocity.y < -maxspeed) velocity.y = -maxspeed;
        if(velocity.y >  maxspeed) velocity.y =  maxspeed;

        //Update sprite position and move sprite
        position += velocity;
        characterSprite.setPosition(position);

        sf::Vector2u currWSize = window.getSize();

        if(position.x <= 0)
        {
            position.x = 0;
        }
        if(position.y <= 0)
        {
            position.y = 0;
        }
        if(position.x >= currWSize.x - characterSprite.getGlobalBounds().width)
        {
            position.x = currWSize.x - characterSprite.getGlobalBounds().width;
        }
        if(position.y >= currWSize.y - characterSprite.getGlobalBounds().height)
        {
            position.y = currWSize.y - characterSprite.getGlobalBounds().height;
        }

        window.display();
    }

    return 0;
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
    //Collision
    sf::FloatRect boundingBoxRBS = redBlockSprite.getGlobalBounds();
    sf::FloatRect boundingBoxPLS = characterSprite.getGlobalBounds();

    if(boundingBoxRBS.intersects(boundingBoxPLS))
    {
       if(position.x >= redBlockSprite.getGlobalBounds().width)
       {

       }
    }
this should be inside the loop. Also, you should redesign your pattern is very flawed and makes reading your code hard. You should separate the repeated stuff into sub-programs. Examples:

1
2
3
4
5
6
7
8
    if(!character.loadFromFile("Resources/Textures/triangle.png"))
    {
        cout << "Error loading resource '::Texture'" << endl;
    }
    else
    {
        cout << "Texture Loaded" << endl;
    }
and
1
2
3
4
5
6
7
8
    if(!redBlock.loadFromFile("Resources/Textures/RedBlock.png"))
    {
        cout << "Error loading Red Block Texture" << endl;
    }
    else
    {
        cout << "Red block texture Loaded" << endl;
    }
could be something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool loadTexture(sf::Texture &texture, std::string const &filename)
{
    bool success = texture.loadFromFile(filename);

    if(success) std::cout << "Texture loaded." << std::endl;
    else std::cout << "Error loading texture." << std::endl;

    return success;
}

...

if(!loadTexture(character, "Resources/Textures/triangle.png))
{
     return -1;
} 
Ok I put it in the loop, also I know I should be doing that but I want to understand the code first before I start doing that. I just cant figure out how to get this collision to work, I'm trying something but it isnt working:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

//Collision
    sf::FloatRect boundingBoxRBS = redBlockSprite.getGlobalBounds();
    sf::FloatRect boundingBoxPLS = characterSprite.getGlobalBounds();
    sf::Vector2f RBPos(redBlockSprite.getPosition());
    sf::Vector2f PLRPos(characterSprite.getPosition());


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

//Get Bounds
        if(boundingBoxRBS.intersects(boundingBoxPLS))
        {
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
            {
                if(PLRPos >= RBPos)
                {
                    characterSprite.setPosition(position.x - redBlockSprite.getGlobalBounds().width);
                }
            }
        }

Shouldn't you test if there will be a collision before moving and if so move it to as close as you can without being inside the other object?

I also don't understand why you are checking collision then having the user press 'A' then doing who knows what.
"Shouldn't you test if there will be a collision before moving and if so move it to as close as you can without being inside the other object?"

How do I do that? The SFML Tutorial was not at all specific on anything.

I changed my code a little bit:

1
2
3
4
5
6
7
8
9
10
11
//Get Bounds
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            if(boundingBoxRBS.intersects(boundingBoxPLS))
            {
                if(PLRPos.x >= RBPos.x)
                {
                    position.x = characterSprite.getPosition() - redBlockSprite.getGlobalBounds().width; //redBlockSprite.getGlobalBounds().width;
                }
            }
        }


I'm alm,ost certain this is right or at least coming close but I keep getting errors:

|151|error: no match for 'operator-' (operand types are 'const Vector2f {aka const sf::Vector2<float>}' and 'float')|
Last edited on
You could do something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
sf::vector2f positionToMove = sprite.getPosition();

//enter a key
positionToMove += velocity;

sf::FloatRect rectToMove(positionToMove, {sprite.getGlobalBounds().x, sprite.getGlobalBounds().y});

if(rectToMove.intersects(other.getGlobalBounds()))
{
    //change the new position now
}

sprite.setPosition(positionToMove);
Though there may be more effective ways.


Last edited on
It keeps giving me errors, I don't know why?

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
#include <iostream>
#include <math.h>
#include <SFML/Graphics.hpp>

using namespace std;

int main()
{
    sf::Vector2f position;
    sf::Vector2f velocity;
    float maxspeed = 4.0f;
    float accel = 1.5f;
    float decel = 0.01f;
    bool vSyncEnabled = true;

    //Setup Window and Framerate
    sf::RenderWindow window(sf::VideoMode(800, 600), "Sprite Movement");
    window.setFramerateLimit(60);
    window.setVerticalSyncEnabled(vSyncEnabled);

    //Load Texture
    sf::Texture character;
    if(!character.loadFromFile("Resources/Textures/triangle.png"))
    {
        cout << "Error loading resource '::Texture'" << endl;
    }
    else
    {
        cout << "Texture Loaded" << endl;
    }

    //Set Sprite for Character Object
    sf::Sprite characterSprite;
    characterSprite.setTexture(character);
    characterSprite.setOrigin(sf::Vector2f(0, 0));
    characterSprite.setPosition(400, 300);

    //Load Red Block Texture
    sf::Texture redBlock;
    if(!redBlock.loadFromFile("Resources/Textures/RedBlock.png"))
    {
        cout << "Error loading Red Block Texture" << endl;
    }
    else
    {
        cout << "Red block texture Loaded" << endl;
    }

    //Set Sprite for Red Block Object
    sf::Sprite redBlockSprite;
    redBlockSprite.setTexture(redBlock);
    redBlockSprite.setOrigin(sf::Vector2f(0, 0));
    redBlockSprite.setPosition(200, 200);

    //Collision
    sf::Vector2f positionToMove = characterSprite.getPosition();
    sf::FloatRect rectToMove(positionToMove, {characterSprite.getGlobalBounds().width, characterSprite.getGlobalBounds().height});
    while(window.isOpen())
    {
        sf::Event event;

        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                {
                    window.close();
                }
                break;
                //Dont stretch the window contents when its resized
                case sf::Event::Resized:
                {
                    sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height);
                    window.setView(sf::View(visibleArea));
                }
                break;
            }
        }

        window.clear(sf::Color::White);

        window.draw(characterSprite);
        window.draw(redBlockSprite);

        //WASD Movement
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            velocity.x -= accel;
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            velocity.x += accel;
        }
        else
        {
            velocity.x *= decel;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        {
            velocity.y -= accel;
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
        {
            velocity.y += accel;
        }
        else
        {
            velocity.y *= decel;
        }

        //Make sure the sprite isn't going too fast
        if(velocity.x < -maxspeed) velocity.x = -maxspeed;
        if(velocity.x >  maxspeed) velocity.x =  maxspeed;
        if(velocity.y < -maxspeed) velocity.y = -maxspeed;
        if(velocity.y >  maxspeed) velocity.y =  maxspeed;

        //Update sprite position and move sprite
        position += velocity;
        positionToMove += velocity;
        characterSprite.setPosition(position);

        sf::Vector2u currWSize = window.getSize();

        if(position.x <= 0)
        {
            position.x = 0;
        }
        if(position.y <= 0)
        {
            position.y = 0;
        }
        if(position.x >= currWSize.x - characterSprite.getGlobalBounds().width)
        {
            position.x = currWSize.x - characterSprite.getGlobalBounds().width;
        }
        if(position.y >= currWSize.y - characterSprite.getGlobalBounds().height)
        {
            position.y = currWSize.y - characterSprite.getGlobalBounds().height;
        }

        //Get Bounds
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            if(rectToMove.intersects(redBlockSprite.getGlobalBounds()))
            {
                characterSprite.getPosition() = positionToMove;
            }
        }

        window.display();
    }

    return 0;
}


C:\Users\thund_000\Desktop\SFML Bounding Box\main.cpp|147|error: passing 'const Vector2f {aka const sf::Vector2<float>}' as 'this' argument of 'sf::Vector2<float>& sf::Vector2<float>::operator=(const sf::Vector2<float>&)' discards qualifiers [-fpermissive]|
Last edited on
The getPosition() method returns a constant reference you will need to use setPosition(positionToMove);

The reason I suggest the method I did is because right now you go out of bounds then when the key is let up it goes back in bounds (hitting walls) and you currently go through objects.

By the way the code I was suggesting was just pseudo code not for you to copy/paste into it. Now you have a position and a positionToMove variable. You only need one.

The thing i was trying to mention is you have
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        sf::Vector2u currWSize = window.getSize();

        if(position.x <= 0)
        {
            position.x = 0;
        }
        if(position.y <= 0)
        {
            position.y = 0;
        }
        if(position.x >= currWSize.x - characterSprite.getGlobalBounds().width)
        {
            position.x = currWSize.x - characterSprite.getGlobalBounds().width;
        }
        if(position.y >= currWSize.y - characterSprite.getGlobalBounds().height)
        {
            position.y = currWSize.y - characterSprite.getGlobalBounds().height;
        }
for checking collision with the walls but..it is after you move not before you move. Put it before characterSprite.setPosition(position); Again you are trying to set the position twice as well.

P.S. you can check the direction it is moving based on the velocity when I say //change the new position now basically if you are moving right that means you hit something on the left so the x position should be the other objects x - the width of that object. So something like position.x = otherPosition.x - widthOfObjectThatCollided;

P.P.S You can also just check the side that it was hit and make a custom collision detection instead of the .intersect() method I will work on an example though might be a little while since I haven't used SFML in quite a while.
Last edited on
Ok i got it working, so if i wanted to do this for every side i would jsut need to change the x and y?
Ah good anyways here is what I was talking about (there are a few bugs in it though)

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
#include <iostream>
#include <string>
#include <SFML/Graphics.hpp>

int main()
{
    unsigned const width = 640u;
    unsigned const height = 480u;
    unsigned const bpp = 32u;
    sf::VideoMode const videoMode(width, height, bpp);

    std::string const title = "Example: Collision";
    sf::RenderWindow window(videoMode, title);

    float const side = 50.f;
    sf::RectangleShape square({side, side});
    sf::Vector2f const squarePos(width/2.f, height/2.f);
    square.setPosition(squarePos);
    sf::FloatRect const squareRect = square.getGlobalBounds();

    float const radius = 10.f;
    sf::CircleShape circle(radius);
    sf::Vector2f position(width/2, radius * 2);
    circle.setPosition(position);
    circle.setFillColor(sf::Color::Red);

    float const maxVelocity = 1.f;
    sf::Vector2f velocity(0.f, 0.f);
    float const acceleration = .05f;
    float const decelleration = .45f;

    float const threshold = 0.05f;
    while(window.isOpen())
    {
        window.clear();
        window.draw(square);
        window.draw(circle);
        window.display();

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

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
            velocity.x -= acceleration;

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
            velocity.x += acceleration;

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
            velocity.y -= acceleration;

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
            velocity.y += acceleration;

        if(velocity.x > maxVelocity)
            velocity.x = maxVelocity;
        if(velocity.y > maxVelocity)
            velocity.y = maxVelocity;
        if(velocity.x < -maxVelocity)
            velocity.x = -maxVelocity;
        if(velocity.y < -maxVelocity)
            velocity.y = -maxVelocity;

        //set the position it will be moved to
        position += velocity;

        //check for collision with wall
        //if so fix the position to move to
        if(position.x < 0)
            position.x = 0;
        if(position.x + radius * 2 > width)
            position.x = width - radius * 2;
        if(position.y < 0)
            position.y = 0;
        if(position.y + radius * 2 > height)
            position.y = height - radius * 2;

        sf::FloatRect playerRect = circle.getGlobalBounds();
        //check for collision
        if(playerRect.intersects(squareRect))
        {
            //check for collision on right of player
            if(velocity.x > 0 && playerRect.left + playerRect.width > squareRect.left)
            {
                position.x = squareRect.left - playerRect.width - threshold; //same as position.x = squarePos.x
            }

            //check for collision on left of player
            if(velocity.x < 0 && playerRect.left < squareRect.left + squareRect.width)
            {
                position.x = squareRect.left + squareRect.width + threshold;
            }

            //check for collision on top of player
            if(velocity.y < 0 && playerRect.top < squareRect.top + squareRect.height)
            {
                position.y = squareRect.top + squareRect.height + threshold;
            }

            //check for collision on bottom of player
            if(velocity.y > 0 && playerRect.top + playerRect.height > squareRect.top)
            {
                position.y = squareRect.top - playerRect.height - threshold;
            }
        }

        circle.setPosition(position);
        velocity *= decelleration;
    }
}


yes you would need to change the x and y not just x or y. Another solution would be if you are already hitting a side then you can't move in the direction the side is from you. Ex: You are hitting an object on the left side then make it so you can't move right anymore (velocity.x must be < 0).
Last edited on
Ok thanks, I was looking it over and it seems pretty straighforeward, but I have a texture, thats the thing, I was able to get the left and right side working but the top and bottom won't unless i comment out the two if statements for left and right, and vice versa, so if i comment out left and right the top and bottom collision will work, do I need to use pixel perfect collision or something? what's going on here?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Get Bounds
        if(rectToMove.intersects(redBlockSprite.getGlobalBounds()))
        {
            if(position.x >= redBlockSprite.getPosition().x)
            {
                cout << "Touched right side" << endl;
                position.x = redBlockSprite.getPosition().x + characterSprite.getGlobalBounds().width;
            }
            if(position.x <= redBlockSprite.getPosition().x)
            {
                cout << "Touched Left Side" << endl;
                position.x = redBlockSprite.getPosition().x - characterSprite.getGlobalBounds().width;
            }
            if(position.y >= redBlockSprite.getPosition().y)
            {
                cout << "Touched Top" << endl;
                position.y = redBlockSprite.getPosition().y + characterSprite.getGlobalBounds().height;
            }
            if(position.y <= redBlockSprite.getPosition().y)
            {
                cout << "Touched Bottom" << endl;
                position.y = redBlockSprite.getPosition().y - characterSprite.getGlobalBounds().height;
            }
        }
Depending on the type of shape you have you are going to need different things. For a square you can use the rect provided. For a circle (even though I didn't) you could just check if the center point + radius hits anything. For other shapes you may need to do other things.
Well, I have two shapes, both 32x32 so what would I do for them? both squares but the collision doesnt work.
Last edited on
So they are squares? You could just use the rect.
oh yes sorry, i updated my post. Yeah both of them are squares and the same size but it doesnt work, when i have all the if statements un commented the sprite acts like the square is slippery and slips over it.
Look at your if statements again. You are forgetting the widths/heights. You are comparing left to left, and top to top. You should be doing right to left, left to right, top to bottom, and bottom to top. You also might want to add the direction in the if statements (velocity).
Last edited on
Ok that doesn't work either, i just don't know what could be going wrong :/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if(rectToMove.intersects(redBlockSprite.getGlobalBounds()))
        {
            if(velocity.x < 0 && position.x <= redBlockSprite.getPosition().x)
            {
                cout << "Touched left side" << endl;
                position.x = redBlockSprite.getPosition().x - characterSprite.getGlobalBounds().width;
            }
            if(velocity.y > 0 && position.y >= redBlockSprite.getPosition().y)
            {
                cout << "Touched Bottom" << endl;
                position.y = redBlockSprite.getPosition().y + characterSprite.getGlobalBounds().height;
            }

            if(velocity.x > 0 && position.x >= redBlockSprite.getPosition().x)
            {
                cout << "Touched right Side" << endl;
                position.x = redBlockSprite.getPosition().x + characterSprite.getGlobalBounds().width;
            }
            if(velocity.y < 0 && position.y <= redBlockSprite.getPosition().y)
            {
                cout << "Touched Top" << endl;
                position.y = redBlockSprite.getPosition().y - characterSprite.getGlobalBounds().height;
            }
        }
I think you are not reading everything. Think about your logic.


if player x position less than or equal to other object x position (has a collision of left some how?)

It should be if the player x position less than other object x position + other object width.

Otherwise you are comparing the players left side to the other others left side. Which would mean there is no collision and you are moving to the left while on the left side (or same x position) as the other object.
So like this?

1
2
3
4
5
if(velocity.x < 0 && position.x < redBlockSprite.getPosition().x + redBlockSprite.getGlobalBounds().width)
            {
                cout << "Touched left side" << endl;
                position.x = redBlockSprite.getPosition().x - characterSprite.getGlobalBounds().width;
            }
yes, that should do the trick.
Pages: 12