C++ Ball Physics Tutorial 4 - Balls Collision

In the previous part we gave the balls some velocity based on our mouse position when we pressed and released the left mouse button. In this part, we will make the balls bounce when they contact each other. We are actually only going to work with a single function in this part, and that is going to be void World::ballCollision(). So open World.cpp.

The final function looks like this:

void World::ballCollision() {
	for (Ball& ball : balls) {
		for (Ball& ball2 : balls) {

			if (&ball != &ball2) {
				if (collision.ballOverlap(ball, ball2)) {

					float distance = collision.distanceSquared(ball.getPosition(), ball2.getPosition());
					float distanceSquared = sqrtf(distance);
					float overlap = (distanceSquared - ball.getRadius() - ball2.getRadius()) / 2.f;

					float moveX = (overlap * (ball.getPosition().x - ball2.getPosition().x) / distanceSquared);
					float moveY = (overlap * (ball.getPosition().y - ball2.getPosition().y) / distanceSquared);

					ball.setPosition(ball.getPosition().x - moveX, ball.getPosition().y - moveY);
					ball2.setPosition(ball2.getPosition().x +  moveX, ball2.getPosition().y + moveY);

					sf::Vector2f normal((ball2.getPosition().x - ball.getPosition().x) / distanceSquared, (ball2.getPosition().y - ball.getPosition().y) / distanceSquared);
					sf::Vector2f tangent(-normal.y, normal.x);

					float dotProductTangent1 = ball.getVelocity().x * tangent.x + ball.getVelocity().y * tangent.y;
					float dotProductTangent2 = ball2.getVelocity().x * tangent.x + ball2.getVelocity().y * tangent.y;

					float dotProductNormal1 = ball.getVelocity().x * normal.x + ball.getVelocity().y * normal.y;
					float dotProductNormal2 = ball2.getVelocity().x * normal.x + ball2.getVelocity().y * normal.y;

					float m1 = (dotProductNormal1 * (ball.getMass() - ball2.getMass()) + 2.0f * ball.getMass() * dotProductNormal2) / (ball.getMass() + ball2.getMass());
					float m2 = (dotProductNormal2 * (ball2.getMass() - ball.getMass()) + 2.0f * ball.getMass() * dotProductNormal1) / (ball.getMass() + ball2.getMass());

					ball.setVelocity(tangent.x * dotProductTangent1 + normal.x * m1, tangent.y * dotProductTangent1 + normal.y * m1);
					ball2.setVelocity(tangent.x * dotProductTangent2 + normal.x * m2, tangent.y * dotProductTangent2 + normal.y * m2);
				}
			}
		}
	}
}

We use two for-loops to check against every ball. To make sure that we're not checking a ball against itself, we add:

if (&ball != &ball2) {

This is going to check if two balls has the same reference, if they do it's the same ball and we skip it. If it's not the same ball, we check if the two balls collide by using the collision.ballOverlap function we created before:

if (collision.ballOverlap(ball, ball2)) {

If they do collide, the very first thing we do is to make sure that one ball is not inside another ball, because that would break the law of physics. We do this by first calculating the distance between the center of the two balls with pythagorean, by using the collision.distanceSquared function. We can then get the overlap distance by subtracting the full distance with the two balls radiuses:

float distance = collision.distanceSquared(ball.getPosition(), ball2.getPosition());
float distanceSquared = sqrtf(distance);
float overlap = (distanceSquared - ball.getRadius() - ball2.getRadius()) / 2.f;

The overlap is divided by 2 since one ball is going to move half of the overlap distance to one direction, and the other ball is going to move half of the overlap to the opposite direction:

float moveX = (overlap * (ball.getPosition().x - ball2.getPosition().x) / distanceSquared);
float moveY = (overlap * (ball.getPosition().y - ball2.getPosition().y) / distanceSquared);

ball.setPosition(ball.getPosition().x - moveX, ball.getPosition().y - moveY);
ball2.setPosition(ball2.getPosition().x +  moveX, ball2.getPosition().y + moveY);

We calculate the normal vector by drawing a line between the two center points, and normalize it with the distance. The tangent vector is the calculated by flipping the x and y of the normal vector, and make one of them inverted.

sf::Vector2f normal((ball2.getPosition().x - ball.getPosition().x) / distanceSquared, (ball2.getPosition().y - ball.getPosition().y) / distanceSquared);
sf::Vector2f tangent(-normal.y, normal.x);

To determine how much of the velocity in each direction that is going to be transferred towards the tangent, we make use of the dot product. To get the velocity towards the normal vector, we use the same formula.

float dotProductTangent1 = ball.getVelocity().x * tangent.x + ball.getVelocity().y * tangent.y;
float dotProductTangent2 = ball2.getVelocity().x * tangent.x + ball2.getVelocity().y * tangent.y;

float dotProductNormal1 = ball.getVelocity().x * normal.x + ball.getVelocity().y * normal.y;
float dotProductNormal2 = ball2.getVelocity().x * normal.x + ball2.getVelocity().y * normal.y;

In order to calculate the final velocities, we need to take conversation of the total momentum into account. Wikipedia has an article about elastic collision, where we will steal a formula from:

float m1 = (dotProductNormal1 * (ball.getMass() - ball2.getMass()) + 2.0f * ball.getMass() * dotProductNormal2) / (ball.getMass() + ball2.getMass());
float m2 = (dotProductNormal2 * (ball2.getMass() - ball.getMass()) + 2.0f * ball.getMass() * dotProductNormal1) / (ball.getMass() + ball2.getMass());

Notice that we use the dotProductNormal1 and dotProductNormal2 scalars in the formula. 

And we can finally give the balls the velocity they deserve after the collision. We just sum the tangent response with the normal response in each direction:

					ball.setVelocity(tangent.x * dotProductTangent1 + normal.x * m1, tangent.y * dotProductTangent1 + normal.y * m1);
					ball2.setVelocity(tangent.x * dotProductTangent2 + normal.x * m2, tangent.y * dotProductTangent2 + normal.y * m2);

 

Enjoyed this article? Give the teacher an apple.

cookie

0

Author

authors profile photo

Articles with similar tags

Comments