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

Author Topic: Determing top/bottom and side collision  (Read 4861 times)

0 Members and 1 Guest are viewing this topic.

natchos

  • Jr. Member
  • **
  • Posts: 97
    • View Profile
    • Email
Determing top/bottom and side collision
« on: April 16, 2012, 10:57:50 am »
Hello I'm currently working on a program in which I have a ball which moves and collides with a square. What I need to determine is whether to flip the Xvelocity or the Yvelocity. The function looks like this

bool ADVBoxCollision(sf::gEntity* sprite1, sf::gEntity* sprite2, bool* pTop, bool* pSide)
{
        sf::Rect<float> FirstRect = sf::Rect<float>(sprite1->GetPosition().x - sprite1->GetCenter().x ,sprite1->GetPosition().y - sprite1->GetCenter().y,sprite1->GetPosition().x+sprite1->GetSize().x - sprite1->GetCenter().x,sprite1->GetPosition().y+sprite1->GetSize().y - sprite1->GetCenter().y);
        sf::Rect<float> SecondRect = sf::Rect<float>(sprite2->GetPosition().x - sprite2->GetCenter().x ,sprite2->GetPosition().y - sprite2->GetCenter().y,sprite2->GetPosition().x+sprite2->GetSize().x - sprite2->GetCenter().x,sprite2->GetPosition().y+sprite2->GetSize().y - sprite2->GetCenter().y);
        //ToS stands for Top or side. It tells whether the sprite collided with the top/bottom part or the side part. 0 = top/bottom, 1 = sides
        if(BoxCollision(FirstRect, SecondRect))
        {
                if((FirstRect.Left <= SecondRect.Right) && (FirstRect.Right >= SecondRect.Left))
                {
                        if(!*pSide) *pSide = 1;
                        else *pSide = 0;                        //If RECT1 is to the left of RECT2
                }
                        if((FirstRect.Top <= SecondRect.Bottom) && (FirstRect.Bottom >= SecondRect.Top))
                {
                        if (*pTop) *pTop = 0;                   //If RECT1 is over RECT2
                        else *pTop = 1;
                }
                return BoxCollision(FirstRect, SecondRect);
        }
        else return false;
}

The pTop and the pSide variables points to a bool value which is use to conditionally reverse the Y or Xvelocity at a detected collision.

The problem is that the side collision always flips, no matter on what side the ball collides with the square and the top bool never flips at all.

Any help?

Also sorry for posting this here, wasn't sure if this fitted anywhere at all.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: Determing top/bottom and side collision
« Reply #1 on: April 16, 2012, 12:01:17 pm »
We can't really tell where what the problem is, since we have no idea what BoxCollision does.

Additionally I'd advice you to use references instead of pointers and since you're not gonna change anything about the sprites, use const references.
Also bool can work with 0 and 1 but it's actually meant to work with false and true.
And as a side note SFML provides typedef Rect<float> FloatRect;, so you don't need to write sf::Rect<float>.
So the code should more look like this:
bool ADVBoxCollision(const sf::gEntity& sprite1, const sf::gEntity& sprite2, bool& pTop, bool& pSide)
{
        sf::FloatRect FirstRect(sprite1.GetPosition().x - sprite1.GetCenter().x ,sprite1.GetPosition().y - sprite1.GetCenter().y,sprite1.GetPosition().x+sprite1.GetSize().x - sprite1.GetCenter().x,sprite1.GetPosition().y+sprite1.GetSize().y - sprite1.GetCenter().y);
        sf::FloatRect SecondRect(sprite2.GetPosition().x - sprite2.GetCenter().x ,sprite2.GetPosition().y - sprite2.GetCenter().y,sprite2.GetPosition().x+sprite2.GetSize().x - sprite2.GetCenter().x,sprite2.GetPosition().y+sprite2.GetSize().y - sprite2.GetCenter().y);
        //ToS stands for Top or side. It tells whether the sprite collided with the top/bottom part or the side part. 0 = top/bottom, 1 = sides
        if(BoxCollision(FirstRect, SecondRect))
        {
                if((FirstRect.Left <= SecondRect.Right) && (FirstRect.Right >= SecondRect.Left))
                {
                        if(!pSide) pSide = true;
                        else pSide = false;                        //If RECT1 is to the left of RECT2
                }
                if((FirstRect.Top <= SecondRect.Bottom) && (FirstRect.Bottom >= SecondRect.Top))
                {
                        if(pTop) pTop = false;                   //If RECT1 is over RECT2
                        else pTop = true;
                }
                return BoxCollision(FirstRect, SecondRect);
        }
        else
                return false;
}

Regarding your problem, it's quite hard for us to spot the problem, since we'd need to go though everything by hand and asume stuuff, since we don't really see with what data you're working.
It's way easier on your side to check. Just start up the debugger, set breakpoints at the if-statements, check why the one if-statement never evaluates to true and trace back from there what happens before.

And since SFML 2 RC got (finally) released yesterday, you may consider switching over to SFML 2. ;)
« Last Edit: April 16, 2012, 12:13:04 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

natchos

  • Jr. Member
  • **
  • Posts: 97
    • View Profile
    • Email
Re: Determing top/bottom and side collision
« Reply #2 on: April 17, 2012, 10:07:51 am »
Ty for the help. I rewrote the program to use references instead, and I had to change some other functions, so I haven't been able/ found it neccesary to write again ^^

Also BoxCollision is a simple call to a function which checks if the two rects sent as parameters intersect.

The problem is I think, that if the rects collide then both the if statements evaluate to true.
What I would be looking for then would be something that would only evaluate to true if it was to the side or only evaluate to true if it was top.
Using a if/else if could work, buuuut I think that the first statement would always go to true. Anyhow I'll go back to coding and see if I find anything.

natchos

  • Jr. Member
  • **
  • Posts: 97
    • View Profile
    • Email
Re: Determing top/bottom and side collision
« Reply #3 on: April 19, 2012, 09:39:25 am »
So, if anyone happens to through their eye over here I'll just keeping detailing what I'm doing.

I decided to reset the bool at the start of every frame, instead pf at every collision. Furthermore I'm making the sprites borders "bigger" by adding +4 or something like that in the if call, so that the intended switch opportunity is detected better.

natchos

  • Jr. Member
  • **
  • Posts: 97
    • View Profile
    • Email
Re: Determing top/bottom and side collision
« Reply #4 on: April 25, 2012, 09:40:41 pm »
After about a week of tweaking the function I finally found something which works in 99.9% of all collisions. The only time that this code doesnt work is at veeeeeeery high speeds. Anyhow here is the code. If anyone would like to use it feel free to do so.

bool ADVBoxCollision(sf::gEntity& player, sf::gEntity& sprite2, bool& pTop, bool& pSide)
{
        bool Firsttotheleft = false;
        bool Firsttotheright = false;
        bool Firstabove = false;
        bool Firstbelow = false;
        sf::FloatRect FirstRect = sf::FloatRect(player.GetPosition().x - (player.GetSize().x/2), player.GetPosition().y - (player.GetSize().y/2),player.GetPosition().x + (player.GetSize().x/2),player.GetPosition().y+(player.GetSize().y/2));
        sf::FloatRect SecondRect = sf::FloatRect(sprite2.GetPosition().x - (sprite2.GetSize().x/2), sprite2.GetPosition().y - (sprite2.GetSize().y/2),sprite2.GetPosition().x + (sprite2.GetSize().x/2),sprite2.GetPosition().y+(sprite2.GetSize().y/2));
        //ToS stands for Top or side. It tells whether the sprite collided with the top/bottom part or the side part. 0 = top/bottom, 1 = sides
if(BoxCollision(FirstRect, SecondRect))
        {
                if((FirstRect.Left - 20) <= (SecondRect.Left - FirstRect.GetWidth()))
                        Firsttotheleft = true;
                if((FirstRect.Right + 20) >= (SecondRect.Right + FirstRect.GetWidth()))
                        Firsttotheright = true;
                if((FirstRect.Top - 20) <= (SecondRect.Top - FirstRect.GetHeight()))
                        Firstabove = true;
                if((FirstRect.Bottom + 20) >= (SecondRect.Bottom + FirstRect.GetHeight()))
                        Firstbelow = true;
                if(Firstbelow || Firstabove)
                        pTop = true;
                if(Firsttotheright || Firsttotheleft)
                        pSide = true;
                return BoxCollision(FirstRect, SecondRect);
        }
        else return false;
}
bool ADVBoxCollision(sf::FloatRect FirstRect, sf::FloatRect SecondRect, bool& pTop, bool& pSide)
{
        bool Firsttotheleft = false;
        bool Firsttotheright = false;
        bool Firstabove = false;
        bool Firstbelow = false;
        this->RECT1 = FirstRect;
        this->RECT2 = SecondRect;
        //ToS stands for Top or side. It tells whether the sprite collided with the top/bottom part or the side part. 0 = top/bottom, 1 = sides
        if(BoxCollision(FirstRect, SecondRect))
        {
                if((FirstRect.Left - 20) <= (SecondRect.Left - FirstRect.GetWidth()))
                        Firsttotheleft = true;
                if((FirstRect.Right + 20) >= (SecondRect.Right + FirstRect.GetWidth()))
                        Firsttotheright = true;
                if((FirstRect.Top - 20) <= (SecondRect.Top - FirstRect.GetHeight()))
                        Firstabove = true;
                if((FirstRect.Bottom + 20) >= (SecondRect.Bottom + FirstRect.GetHeight()))
                        Firstbelow = true;
                if(Firstbelow || Firstabove)
                        pTop = true;
                if(Firsttotheright || Firsttotheleft)
                        pSide = true;
                return BoxCollision(FirstRect, SecondRect);
        }
        else return false;
}

(the RECT1 and RECT2 are members of the physics manager which I've created, so ignore/delete those calls if you feel like it)

Anyhow, tyvm for the help I got and for the tip to use references instead of pointers.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: Determing top/bottom and side collision
« Reply #5 on: April 25, 2012, 09:50:10 pm »
The only time that this code doesnt work is at veeeeeeery high speeds. Anyhow here is the code.
The effect is called tunneling. If you want to prevent this, you'll have to google a bit, there many solutions floating around.

Quote
If anyone would like to use it feel free to do so.
I just might use this, thanks. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

natchos

  • Jr. Member
  • **
  • Posts: 97
    • View Profile
    • Email
Re: Determing top/bottom and side collision
« Reply #6 on: April 25, 2012, 10:08:29 pm »
Ah ok, well I will check around on the internet about this mysterious "tunneling" then ^^. Thank you for the information.

Cool, if you run into any weird problems with this function feel free to send me a pm :)

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Determing top/bottom and side collision
« Reply #7 on: April 27, 2012, 06:00:40 pm »
By the way, if you dig deeper into complex collision detection and response, you could take a look at physics libraries like Box2D. The latter also has an option to handle tunneling ("bullet" flag).
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development: