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

Author Topic: Bounding box collision problem  (Read 4387 times)

0 Members and 1 Guest are viewing this topic.

Rementh

  • Newbie
  • *
  • Posts: 6
    • View Profile
Bounding box collision problem
« on: July 30, 2014, 09:33:16 am »
Hi, im working on a project (2D platformer game), and i'm trying to achieve proper collisions on my map. The problem is, i want my collisions to be perfect (work in every possible situation). I wrote a function which meets my expectations almost perfectly, but there is a problem with corners. When i'm trying to move in two directions at once, up and right for example, and my character meets the box corner, it stops and i can't go further until i release one of the buttons to specify the one exact direction. It only happens when im moving counter clockwise (pressing two butons, like up+right, up+left, left+down). Here's the code to visualize the problem:
(click to show/hide)
"triangle" sprite has rectangular 100x100 texture, "box" sprites also.
« Last Edit: July 30, 2014, 09:46:11 am by Rementh »

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Bounding box collision problem
« Reply #1 on: July 30, 2014, 04:12:55 pm »
It seems to be that when it travels into a corner, it corrects it by moving both horizontal and vertical back to a safe position. This cancels movement in either direction, which is why it stops. This only happens when it travels into a corner though, not when it travels diagonally into a side. The reason it only happens in one direction is due to the order of the boxes being tested for collision. If you reverse the order, I believe the direction will also be reversed i.e. stopping during clockwise motion.

Try adding these .setColors to your code to see which ones are colliding.

if (sprite_1_top < sprite_2_bottom &&
        sprite_1_bottom > sprite_2_top &&
        sprite_1_left < sprite_2_right &&
        sprite_1_right > sprite_2_left)
{
    sprite_2.setColor(sf::Color::Red); // glow red when colliding
        if (sprite_1_prev...
        ...
        ...ition().y);
        }
}
else
    sprite_2.setColor(sf::Color::White); // show as normal when not colliding
 

If you want one of the directions to continue, you may wish to consider only resetting one of them. Which one could be based upon which one has travelled furthest or specific properties given by the map e.g. vertical correction only for floors.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Rementh

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Bounding box collision problem
« Reply #2 on: July 30, 2014, 09:01:25 pm »
Well, now i'm kinda confused. I added the colors to box collision function, but it changes the color for only one sprite at the time. Even if i collide with two sprites at the same time, one of them is red, other default (only in corners it sets two sprites red). So maybe my dollision detection itn't working properly.
Here's the example of how it looks: http://scr.hu/2d0y/c6l0u
In 1 and 2 i'm pressing left/right arrows, in 3 up and right.

AFS

  • Full Member
  • ***
  • Posts: 115
    • View Profile
Re: Bounding box collision problem
« Reply #3 on: July 30, 2014, 09:28:12 pm »
This is totally unrelated, but you really need to learn how to use the STL containers, man.

(click to show/hide)
« Last Edit: July 30, 2014, 09:42:57 pm by AFS »

Rementh

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Bounding box collision problem
« Reply #4 on: July 30, 2014, 10:18:29 pm »
Yeah, i have to learn much more, thanks for the cointainers, very helpful.

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Bounding box collision problem
« Reply #5 on: July 30, 2014, 10:22:26 pm »
it changes the color for only one sprite at the time
The reason for that is actually quite simple. You are testing one collision at a time and fixing it based on that collision. When you test the next one, the fix could have already stopped it colliding with that one. e.g. in your image "1", it tests the top one first, realises that it's colliding and moves it back away (left). Then it test the lower one and it's no longer colliding.

This is totally unrelated, but you really need to learn how to use the STL containers, man.
It's funny you should say that as I rewrote this code with STL containers so I could actually see it more clearly  8)

AFS, I just noticed that you created a grid of collision objects in your creation of boxes in the STL container code. That's too many objects  :P
Also, this:
Quote
for (unsigned int i = 0; i < boxes.size(); i++)
    window.draw( boxes[i] );
would be better as this:
for (auto box : boxes)
    window.draw(box);
DISCLAIMER: I like to assume everyone is using C++11.
« Last Edit: July 30, 2014, 10:31:52 pm by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Bounding box collision problem
« Reply #6 on: July 30, 2014, 10:42:32 pm »
for (unsigned int i = 0; i < boxes.size(); i++)
    window.draw( boxes[i] );
would be better as this:
for (auto box : boxes)
    window.draw(box);
DISCLAIMER: I like to assume everyone is using C++11.
Or even better:
for (const auto& box : boxes)
...
There's no reason to make a copy of every element in the container just to draw them.

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Bounding box collision problem
« Reply #7 on: July 30, 2014, 11:09:18 pm »
Agreed. Missing the "&" off was a typo but I normally don't use const there. Never really thought about it. Maybe I should...
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Rementh

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Bounding box collision problem
« Reply #8 on: July 31, 2014, 01:53:39 pm »
Thanks for the advices, i modified the code a little bit. Now it looks less nebwie :D But still don't know how to implement proper moving during collision. It's good only for the floor, but maybe it's enough. I think i would just have to avoid building map with few separated objects in one wall. Here's the code after changes:
(click to show/hide)

Strelok

  • Full Member
  • ***
  • Posts: 139
    • View Profile
    • GitHub
Re: Bounding box collision problem
« Reply #9 on: July 31, 2014, 03:38:05 pm »
#define window_size_x 1200
#define window_size_y 800
#define max_speed     1000
#define gravity       4500
#define jump_power    1500
#define friction          5000
 
Use const variables intead of preprocessor defines This is why
void BoxCollision(sf::Sprite & sprite_1, sf::Sprite & sprite_2, const float x, const float y, float & gravity_delta_float, float & h_speed_float, bool & jump_bool, bool & double_jump_bool)
{
        float sprite_1_top = sprite_1.getPosition().y - sprite_1.getOrigin().y;
        float sprite_1_bottom = sprite_1.getPosition().y - sprite_1.getOrigin().y + sprite_1.getTexture()->getSize().y;
        float sprite_1_left = sprite_1.getPosition().x - sprite_1.getOrigin().x;
        float sprite_1_right = sprite_1.getPosition().x - sprite_1.getOrigin().x + sprite_1.getTexture()->getSize().x;

        float sprite_1_previous_top = y - sprite_1.getOrigin().y;
        float sprite_1_previous_bottom = y - sprite_1.getOrigin().y + sprite_1.getTexture()->getSize().y;
        float sprite_1_previous_left = x - sprite_1.getOrigin().x;
        float sprite_1_previous_right = x - sprite_1.getOrigin().x + sprite_1.getTexture()->getSize().x;

        float sprite_2_top = sprite_2.getPosition().y - sprite_2.getOrigin().y;
        float sprite_2_bottom = sprite_2.getPosition().y - sprite_2.getOrigin().y + sprite_2.getTexture()->getSize().y;
        float sprite_2_left = sprite_2.getPosition().x - sprite_2.getOrigin().x;
        float sprite_2_right = sprite_2.getPosition().x - sprite_2.getOrigin().x + sprite_2.getTexture()->getSize().x;

        if (sprite_1_top < sprite_2_bottom &&
                sprite_1_bottom > sprite_2_top &&
                sprite_1_left < sprite_2_right &&
                sprite_1_right > sprite_2_left)
        {              
                sprite_2.setColor(sf::Color::Red);
                if (sprite_1_previous_top >= sprite_2_bottom)
                {
                        sprite_1.setPosition(sprite_1.getPosition().x, sprite_2_bottom + sprite_1.getOrigin().y);
                        gravity_delta_float = jump_power;
                }
                if ((sprite_1_previous_left >= sprite_2_right && sprite_1_previous_bottom <= sprite_2_top) || (sprite_1_previous_right <= sprite_2_left && sprite_1_previous_bottom <= sprite_2_top))
                        sprite_1.setPosition(sprite_1.getPosition().x, sprite_2_top - sprite_1.getTexture()->getSize().y + sprite_1.getOrigin().y);
                else
                {
                        if (sprite_1_previous_bottom <= sprite_2_top)
                        {
                                sprite_1.setPosition(sprite_1.getPosition().x, sprite_2_top - sprite_1.getTexture()->getSize().y + sprite_1.getOrigin().y);
                                gravity_delta_float = 0;
                                double_jump_bool = false;
                                jump_bool = false;
                        }
                        if (sprite_1_previous_left >= sprite_2_right)
                        {
                                sprite_1.setPosition(sprite_2_right + sprite_1.getOrigin().x, sprite_1.getPosition().y);
                                h_speed_float = 0;
                        }
                               
                        if (sprite_1_previous_right <= sprite_2_left)
                        {
                                sprite_1.setPosition(sprite_2_left - sprite_1.getTexture()->getSize().x + sprite_1.getOrigin().x, sprite_1.getPosition().y);
                                h_speed_float = 0;
                        }
                }
        }
        else
                sprite_2.setColor(sf::Color::White);
}
 
You are not using classes, you could also create a mySprite struct with top left right bottom values.
You might want to check out The docs and the wiki
int main()
{
        /**********************/
        std::vector <sf::Sprite> boxes;
int amount = 20;
        int separation = 150;
        float posX = -50;
        float posY = 50;
        for (int i = 0; i < amount; i++)
        {
                boxes.push_back( sf::Sprite() );
                if (i < 7) posX += separation;
                else if (i < 11) posY += separation;
                else if (i < 17) posX -= separation;
                else if (i < 20) posY -= separation;
                boxes[i].setTexture(box_texture);
                boxes[i].setPosition(posX, posY);
}
 
Beside all the magic numbers, std::vector lets you specify its initial capacity, so that you won-have to reallocate the array in the heapmemory every push_back()
#include <SFML/Graphics.hpp>
int main()
{
        /**********************/
        std::vector <sf::Sprite> boxes(20);
        int separation = 150;
        float posX = -50;
        float posY = 50;
        for (unsigned int i = 0; i < boxes.capacity(); i++)
        {
                if (i < 7) posX += separation;
                else if (i < 11) posY += separation;
                else if (i < 17) posX -= separation;
                else if (i < 20) posY -= separation;
                boxes[i].setTexture(box_texture);
                boxes[i].setPosition(posX, posY);
        }
}
 

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Bounding box collision problem
« Reply #10 on: August 01, 2014, 10:35:41 pm »
Just as a follow on from what Strelok said about vectors, you can also use the constructors for all of the objects that are created in the vector during that creation, thusly:
std::vector<sf::Sprite> boxes(20, sf::Sprite(box_texture));
This means you can also remove the line of code that specifies the textures each time  :D
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*