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

Author Topic: Simple Collision Detection for SFML 2  (Read 36751 times)

0 Members and 1 Guest are viewing this topic.

ahnonay

  • Guest
Simple Collision Detection for SFML 2
« on: May 26, 2013, 06:15:53 pm »
I always liked the Collision Detection code from the wiki, especially the OBB-test, so I made some changes to get it running under SFML 2. The pixel perfect test now uses bitmasks instead of sf::Image::getPixel.

I suspect there are a couple of mistakes or bad ideas here and there, so I'd be happy to hear, what you think.
https://github.com/SFML/SFML/wiki/Source%3A-Simple-Collision-Detection-for-SFML-2

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Simple Collision Detection for SFML 2
« Reply #1 on: May 26, 2013, 06:33:58 pm »
Thanks for porting the code!

You could use std::vector instead of new[] and delete[]. The class Collision should rather be a namespace, as it contains only static methods.

Also, the function BoundingBoxTest() is huge -- are you sure this cannot be done simpler? Or at least split a little bit? It's very difficult to understand that code, especially with variable names like A, B. There seems to be a lot of code duplication (just by scrolling over it, you see the parts that look similar).

By the way, _COLLISION_H is a reserved identifier, since it begins with underline and uppercase letter. Just use COLLISION_H. Also, I would not define macros in the header with such general names like PI -- if a user of this header has a constant with this name, there will be name conflicts. Not to mention that const should be preferred over #define for constants ;)
« Last Edit: May 26, 2013, 06:37:32 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

ahnonay

  • Guest
Re: Simple Collision Detection for SFML 2
« Reply #2 on: May 27, 2013, 10:00:46 am »
These were a lot of good suggestions, thank you! I implemented most of them. However, I think I will stick with arrays instead of std::vector for the bitmasks.
About the BoundingBoxTest(): I admit I mostly don't know what is going on in that code-mess. The author of the original version wrote that, apparently following some tutorial. I just made some adjustments to regard a sprite's origin and scale.

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
Re: Simple Collision Detection for SFML 2
« Reply #3 on: May 27, 2013, 07:39:02 pm »
Naked news-delete combos are the lowest level of abstraction possible in a system, so in general should only exist at the lowest level of abstraction, like when describing basic data structures.

The names of variables is not very descriptive. A, B, C and D for example. And t. What does the letter 't' tell me about the purpose of this variable?

The implementation is littered with comments, and as a general rule if code needs comments to explain what it is doing, it's better to make the code more readable than to add the comment. Those comments are good indicators of where you could probably break down the giant functions into a series of function calls to improve the readability and let the function names describe their functionality.
« Last Edit: May 27, 2013, 07:45:51 pm by MorleyDev »
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Simple Collision Detection for SFML 2
« Reply #4 on: May 27, 2013, 07:58:00 pm »
I agree. It's also interesting that the original author called this kind of collision detection "simple" :)

Actually I even think it might be worth the time to rewrite some parts completely, if somebody is really interested in providing a collision detection code for the community. Otherwise sites like the N tutorial are probably the better resource, since they are more understandable.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
Re: Simple Collision Detection for SFML 2
« Reply #5 on: May 27, 2013, 09:04:36 pm »
To highlight this, I quickly refactored the Circle test.

#include <Collision.hpp>

struct BoundingCircle
{
        sf::Vector2f centre;
        float radius;
};

sf::Vector2f GetSpriteCenter(const sf::Sprite& object)
{
        auto dimensions = object.getGlobalBounds();

        return sf::Vector2f (dimensions.left + dimensions.width / 2.0f,
                                                 dimensions.top + dimensions.height / 2.0f);
}

sf::Vector2f GetSpriteSize(const sf::Sprite& object)
{
        auto originalSize = object.getTextureRect();
        auto scale = object.getScale();

        return sf::Vector2f (originalSize.width * scale.x, originalSize.height * scale.y);
}

float GetRadiusOfRectangleWithSize(sf::Vector2f size)
{
        return (size.x + size.y) / 4;
}

BoundingCircle GetBoundingCircle(const sf::Sprite& object)
{
        auto objSize = GetSpriteSize(object);
        auto radius = GetRadiusOfRectangleWithSize(objSize);

        return BoundingCircle({ GetSpriteCenter(object), radius });
}

bool AreBoundingCirclesIntersecting(BoundingCircle boundingCircleOne, BoundingCircle boundingCircleTwo)
{
        auto distance = boundingCircleOne.centre - boundingCircleTwo.centre;
        auto magnitudeOfDistanceSquared = distance.x * distance.x + distance.y * distance.y;
        auto maximumCollidingDistanceBetweenBoundings = (boundingCircleOne.radius       + boundingCircleTwo.radius)     * (boundingCircleOne.radius + boundingCircleTwo.radius);

        return (magnitudeOfDistanceSquared <= maximumCollidingDistanceBetweenBoundings);
}

bool CircleTest(const sf::Sprite& object1, const sf::Sprite& object2)
{
        auto boundingCircleOne = GetBoundingCircle(object1);
        auto boundingCircleTwo = GetBoundingCircle(object2);

        return AreBoundingCirclesIntersecting(boundingCircleOne, boundingCircleTwo);
}
 

Note the code is longer, but functionality should be unchanged. I cannot, admittedly, guarantee this as I neglected to take the time to properly surround the code in tests before refactoring. The really interesting part is it reveals the nature of this function: It's actually two,
1. Get the bounding circle of the sprites
2. Check if the bounding circles intersect
« Last Edit: May 28, 2013, 10:42:44 pm by MorleyDev »
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

ahnonay

  • Guest
Re: Simple Collision Detection for SFML 2
« Reply #6 on: July 28, 2013, 02:01:13 pm »
I finally got around to rewrite the BoundingBoxTest using the Separating Axis Theorem. The code is a lot more readable now, I think.
https://github.com/SFML/SFML/wiki/Source:-Simple-Collision-Detection-for-SFML-2

Jove

  • Full Member
  • ***
  • Posts: 114
    • View Profile
    • http://www.jestofevekites.com/
Re: Simple Collision Detection for SFML 2
« Reply #7 on: August 19, 2013, 09:51:23 pm »
Wow, the bounding box code is actually readable now.  ;)

Nice work. I'm yet to test the BB as most of my current stuff is circular collisions, but the original wiki BB code is right there in my project and I'd be happy to replace it with this!

Thanks for the contribution.
{much better code}

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: Simple Collision Detection for SFML 2
« Reply #8 on: August 28, 2013, 12:11:10 pm »
You shouldn't use auto that excessively, especially considering you won't have to use it in all those cases, making your code only work in C++11 without any significant reason (I spotted so far). Remember those snippets are primarily aimed at newbies or those looking for some quick snippet to include without having to adjust it to their compiler.

Jove

  • Full Member
  • ***
  • Posts: 114
    • View Profile
    • http://www.jestofevekites.com/
Re: Simple Collision Detection for SFML 2
« Reply #9 on: August 05, 2014, 08:08:03 pm »
Today in VS2013 after installing Update 3, I discovered that Code Analysis is now working for me. Gave it a try and was returned only one error which originated from the pixel-perfect test in the wiki code.

Error:

collision.cpp(49): warning C6386: Buffer overrun while writing to 'mask':  the writable size is '*tex-

>public: class sf::Vector2<unsigned int> __thiscall sf::Texture::getSize(void)const ().y**tex->public: class

sf::Vector2<unsigned int> __thiscall sf::Texture::getSize(void)const ().x*1'
bytes, but '2' bytes might be

written.

sf::Uint8* CreateMask(const sf::Texture* tex, const sf::Image& img)
                {
                        sf::Uint8* mask = new sf::Uint8[tex->getSize().y*tex->getSize().x];

                        for (unsigned int y = 0; y < tex->getSize().y; y++)
                        {
                                for (unsigned int x = 0; x < tex->getSize().x; x++)
this line--->           mask[x + y*tex->getSize().x] = img.getPixel(x, y).a;
                        }

                        Bitmasks.insert(std::pair<const sf::Texture*, sf::Uint8*>(tex, mask));

                        return mask;
                }

I don't personally use this part of the wiki code, but I thought it worth highlighting in case it causes other folks a problem.
{much better code}

Switchboy

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Simple Collision Detection for SFML 2
« Reply #10 on: August 18, 2021, 03:03:33 pm »
Sorry for bumping an old thread, but I've written a solution as an add-on to the collision detection from the wiki that checks a single pixel. Usefull for mouse interactions.


bool singlePixelTest(const sf::Sprite& Object1, sf::Vector2f& mousePosition, sf::Uint8 AlphaLimit) {

        if (Object1.getGlobalBounds().contains(mousePosition.x, mousePosition.y)) {
                sf::IntRect O1SubRect = Object1.getTextureRect();

                sf::Uint8* mask1 = Bitmasks.GetMask(Object1.getTexture());

                sf::Vector2f o1v = Object1.getInverseTransform().transformPoint(mousePosition.x, mousePosition.y);
                // Make sure pixels fall within the sprite's subrect
                if (o1v.x > 0 && o1v.y > 0 && o1v.x < O1SubRect.width && o1v.y < O1SubRect.height) {
                        if (Bitmasks.GetPixel(mask1, Object1.getTexture(), (int)(o1v.x) + O1SubRect.left, (int)(o1v.y) + O1SubRect.top) > AlphaLimit) {
                                return true;
                        }
                }
        }
        return false;
}