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);
}
}
}
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++)