2D Ball Collision Causes Balls to Overlap or Stick at High Spawn Rates

1 day ago 2
ARTICLE AD BOX

I'm using Ball::update() and Ball::checkCollision() for 2D ball collisions:

#include <SFML/Graphics.hpp> #include <list> unsigned int BALL_RADIUS = 15; class Ball { public: Ball(float posX = 800/2, float posY = 600/2); void update(sf::RenderWindow&, std::list<Ball>&); sf::Vector3f checkCollision(std::list<Ball>&); ~Ball(); private: float BALL_SPEED_X = 0.05; float BALL_SPEED_Y = 0.05; sf::CircleShape ballSprite; sf::Vector2f trueBallSize; }; Ball::Ball(float posX, float posY) { ballSprite = sf::CircleShape(BALL_RADIUS); ballSprite.setFillColor(sf::Color(255, 255, 0, 255)); ballSprite.setOrigin(BALL_RADIUS, BALL_RADIUS); ballSprite.setPosition(posX, posY); } void Ball::update(sf::RenderWindow& window, std::list<Ball>& balls) { sf::Vector3f velocity = checkCollision(ballsList); if (velocity.z) { BALL_SPEED_X = velocity.x; BALL_SPEED_Y = velocity.y; } float ballX = ballSprite.getPosition().x; float ballY = ballSprite.getPosition().y; ballX += BALL_SPEED_X; ballY += BALL_SPEED_Y; if ((ballX - BALL_RADIUS) <= 0 || (ballX + BALL_RADIUS) >= SCREEN_WIDTH) BALL_SPEED_X *= -1; if ((ballY - BALL_RADIUS) <= 0 || (ballY + BALL_RADIUS) >= SCREEN_HEIGHT) BALL_SPEED_Y *= -1; ballSprite.setPosition(sf::Vector2f(ballX, ballY)); window.draw(ballSprite); } sf::Vector3f Ball::checkCollision(std::list<Ball>& balls) { sf::Vector2f centerA = ballSprite.getPosition(); for (auto& ball : balls) { // When Checking ball against itself // Distance become 0, d <= radius is // always true. if (&ball == this) continue; sf::Vector2f centerB = ball.ballSprite.getPosition(); float dx = centerB.x - centerA.x; float dy = centerB.y - centerA.y; float distance = dx * dx + dy * dy; float radius = BALL_RADIUS; if (distance < (radius * radius)) { sf::Vector2f direction = ball.ballSprite.getPosition() - ballSprite.getPosition(); float length = sqrt((direction.x * direction.x) + (direction.y * direction.y)); direction = direction / length; float dot = BALL_SPEED_X * direction.x + BALL_SPEED_Y * direction.y; sf::Vector2f velocity(BALL_SPEED_X, BALL_SPEED_Y); velocity -= 2.f * dot * direction; return sf::Vector3f(velocity.x, velocity.y, 1); } } return sf::Vector3f(0, 0, 0); } std::list <Ball>ballsList; int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "Collision Core"); int groups = 100 / 10; int gap = BALL_RADIUS * 4; for (int i = 0; i < groups; i++) for (int j = 0; j < 10; j++) ballsList.push_back(Ball(gap + (gap * j), gap + (gap * i))); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) if (event.type == sf::Event::Closed) window.close(); window.clear(); for (Ball& ball : ballsList) ball.update(window, ballsList); window.display(); } return EXIT_SUCCESS; }

I'm using SFML-2.6.1

At high spawn rates, some balls overlap and get stuck inside each other or against the window edges.

At low NUMBER 's, like 40, there is no problem, but at higher values, like 100, the issue becomes visible.

Is this because the collision response updates velocity before the edge-bounce check, causing balls to move out of bounds?

What is the correct way to prevent balls from sticking together/screen edge after collisions?

Read Entire Article