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

Author Topic: sf::CircleShape collision problem  (Read 1208 times)

0 Members and 1 Guest are viewing this topic.

EvgehaRYTP

  • Newbie
  • *
  • Posts: 7
    • View Profile
    • Email
sf::CircleShape collision problem
« on: April 01, 2023, 05:13:37 pm »
I want to implement a collision of balls in such a way that if the condition is true, they would cast in opposite directions, but I have undefined behavior here, I can’t figure out what is the reason (there is a check for a collision with the screen)

config file
    inline float distance(const sf::Vector2f& vec1, const sf::Vector2f& vec2)
    {
        return std::sqrt(std::pow(vec1.x - vec2.x, 2) +  std::pow(vec1.y - vec2.y, 2));
    }

    inline float length(const sf::Vector2f& v)
    {
        return std::sqrt(v.x * v.x + v.y * v.y);
    }

    inline sf::Vector2f normalize(const sf::Vector2f& vec)
    {
        return vec / std::sqrt(vec.x * vec.x + vec.y * vec.y);
    }

    inline sf::Vector2f project(const sf::Vector2f& vec1, const sf::Vector2f& vec2)
    {
        float dot = vec1.x * vec2.x + vec1.y * vec2.y;
        float dot_ = vec2.x * vec2.x + vec2.y * vec2.y;
        return dot / dot_ * vec2;
    }

inline bool circleCollision(const sf::CircleShape& circle1, const sf::CircleShape& circle2)
{
    float dx = circle1.getPosition().x - circle2.getPosition().x;
    float dy = circle1.getPosition().y - circle2.getPosition().y;
    float distance = std::sqrt(dx * dx + dy * dy);

    float radiusSum = circle1.getRadius() + circle2.getRadius();
    if (distance <= radiusSum)
    {
        // collision detected
        return true;
    }
    else
    {
        return false;
    }
}
 

Update in game
for (std::size_t i = 0; i < balls.size(); i++)
        {
                for (std::size_t j = i + 1; j < balls.size(); j++)
                {
                        if (circleCollision(balls[i]->getShape(), balls[j]->getShape()))
                        {


                                sf::Vector2f normal = normalize(balls[j]->getPosition() - balls[i]->getPosition());
                                sf::Vector2f tangent(-normal.y, normal.x);

                                sf::Vector2f v1 = project(balls[i]->getVelocity(), tangent);
                                sf::Vector2f v2 = project(balls[j]->getVelocity(), tangent);

                                sf::Vector2f u1 = project(balls[i]->getVelocity(), normal);
                                sf::Vector2f u2 = project(balls[j]->getVelocity(), normal);

                                float m1 = balls[i]->getMass();
                                float m2 = balls[j]->getMass();

                                float e = 1.f; // restitution coefficient, determines how much kinetic energy is conserved

                                sf::Vector2f new_u1 = ((m1 - e * m2) * u1 + (1.f + e) * m2 * u2) / (m1 + m2);
                                sf::Vector2f new_u2 = ((m2 - e * m1) * u2 + (1.f + e) * m1 * u1) / (m1 + m2);

                                balls[i]->setVelocity(new_u1 + project(v1, tangent));
                                balls[j]->setVelocity(new_u2 + project(v2, tangent));

                                float overlap = balls[i]->getRadius() + balls[j]->getRadius() - distance(balls[i]->getPosition(), balls[j]->getPosition());

                                balls[i]->setPosition(balls[i]->getPosition() - overlap * 0.5f * normal);
                                balls[j]->setPosition(balls[j]->getPosition() + overlap * 0.5f * normal);

                                // add an impulse to separate the balls
                                float impulseMag = overlap / (m1 + m2) * 2.f;
                                balls[i]->applyImpulse(-impulseMag * normal);
                                balls[j]->applyImpulse(impulseMag * normal);
                        }
                }
        }
 

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: sf::CircleShape collision problem
« Reply #1 on: April 02, 2023, 07:29:17 am »
What undefined behaviour do you have? ???

If it's crashing, consider that you might be dividing by zero. These things happen when vectors might be zero.

Also, when comparing lengths of vectors, you don't actually need to do the costly square root.
For example,
float distanceSquared = dx * dx + dy * dy;
float radiusSum = circle1.getRadius() + circle2.getRadius();
float radiusSumSquared = radiusSum * radiusSum;
if (distanceSquared <= radiusSumSquared)
Squares/multiplying is less costly than square roots.

You can also re-use some of that inline code.
normalize:
return vec / length(vec);
distance:
return length(vec1 - vec2);
Also, if you create a dot function, you can re-use that too (in length and project).
e.g.
length:
return std::sqrt(dot(v));
project:
return (dot(vec1) / dot(vec2)) * vec2;

Note that my suggestion of "Distance Squared" is just the simple dot too.



Also, in addition, you're checking every i ball with every i+1 ball so i should never be allowed to be the last ball...
Try:
for (std::size_t i = 0; i < balls.size() - 1u; i++)
« Last Edit: April 02, 2023, 07:32:52 am by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*