- Test for wall collisions
- Test for ball collisions
- Find the earliest collision
- Move the ball to the first collision. If there are no collision then we move by a certain amount of time
- Calculate the bounce for the collision (if any)
- Repeat steps 1 to 5. We sleep for awhile to allow the screen to be painted and ensure certain game speed

Motion is represented by a speed and direction. Therefore we can use a vector to represent the ballâ€™s motion. In the case of the bouncing balls animation, the length of the ballâ€™s velocity is the length the ball travels in one unit of time (a millisecond).

Length(A) = sqrt(x^{2}* y^{2}) A . B = A_{x}B_{x}+ A_{y}B_{y}

Wall collisions occur if in the time frame (t) the ball will pass through the wall.

// Calculate the movement vector for t Vector movec = velocity.scale(t); double collision_t = t; // no collision // time = distance / speed if (center.x - radius + movec.x <= 0) { collision_t = (center.x - radius) / velocity.x; } else if (center.x + radius + movec.x >= boundWidth) { collision_t = (boundWidth - (center.x + radius)) / velocity.x; } else if (center.y - radius + movec.y <= 0) { collision_t = (center.y - radius) / velocity.y; } else if (center.y + radius + movec.y >= boundHeight) { collision_t = (boundHeight - (center.y + radius)) / velocity.y; } return Math.abs(collision_t);

- Calculate the distance between the two balls using Pythagorean Theorem (a^2 + b^2 = c^2)
- Reduce the collision from dynamic-dynamic (eg. both balls are moving) to dynamic-static collision detection (eg. only 1 ball is moving).
- If the length of the movement vector is less then the distance between the objects, then there is no way they can hit
- Find the point where the moving ball is touching the static ball
- Calculate and return when the balls touch

// Calculate the movement vector a for t Vector movea = this.velocity.scale(t); // Calculate the movement vector b for t Vector moveb = b.velocity.scale(t); // Detect wether the balls are touching double deltaX = b.center.x - this.center.x; double deltaXSquared = deltaX * deltaX; // square double deltaY = b.center.y - this.center.y; double deltaYSquared = deltaY * deltaY; // square int sumRadii = this.radius + b.radius; int sumRadiiSquared = sumRadii * sumRadii; // square // Calculate distance between objects double distSquared = deltaXSquared + deltaYSquared; // Reduce dynamic-dynamic collision to dynamic-static Vector movec = movea.subtract(moveb); // If the length of the movement vector is less // then the distance between the objects, // then there is no way they can hit if (movec.getLength() < Math.sqrt(distSquared) - sumRadii) { return t; // no collision } // Normalize the movevec Vector N = movec.normalize(); // Find C, the vector from the center of the moving // ball A to the center of ball B Vector C = this.center.vectorTo(b.center); // D is the point along movec that is closest to ball b // D = N . C = ||C|| * cos(angle between N and C) double D = N.dot(C); // Find the length of the vector C double lengthC = C.getLength(); // Math.sqrt(F) is the distance between the center of ball b // and the closest point on movec double F = (lengthC * lengthC) - (D * D); // If the closest that A will get to B // is more than the sum of their radii, there's no // way they are going collide if (F >= sumRadiiSquared) { return t; // no collision } // We now have F and sumRadii, two sides of a right triangle. // Use these to find the third side, sqrt(T) double T = sumRadiiSquared - F; // If there is no such right triangle with sides length of // sumRadii and sqrt(f), T will probably be less than 0. // Better to check now than perform a square root of a // negative number. if (T < 0) { return t; // no collision } // Therefore the distance the circle has to travel along // the movement vector is D - sqrt(T) double distance = D - Math.sqrt(T); // Get the magnitude of the movement vector double mag = movec.getLength(); // Finally, make sure that the distance A has to move // to touch B is not greater than the magnitude of the // movement vector. if (mag < distance) { return t; } // Return the time it takes ball A to travel this distance double collision_t = (distance/mag) * t; if (collision_t < 0 || collision_t > t) { return t; } // add collision to head of collision list collisions.add(0, new Collision(this, b, collision_t)); return collision_t;

Wall collisions are handled by bouncing it back in opposite direction:

if (c.x - radius <= 0 && velocity.x < 0d) { velocity = new Vector(-velocity.x, velocity.y); } else if (c.x + radius >= boundWidth && velocity.x > 0d) { velocity = new Vector(-velocity.x, velocity.y); } if (c.y - radius <= 0 && velocity.y < 0d) { velocity = new Vector(velocity.x, -velocity.y); } else if (c.y + radius >= boundHeight && velocity.y > 0d) { velocity = new Vector(velocity.x, -velocity.y); }

Ball collisions on other hand are more complex. We are assuming
rigid perfect balls.

Using **Conservation of Momentum**
and **Conservation of Energy** the new
vectors of balls A (v1') and B (v2') are:

optimizedP = 2(a1 - a2) / (A.mass + B.mass) v1' = v1 - optimizedP * A.mass * N; v2' = v2 + optimizedP * B.mass * N;N is the normalized vector along which Momentum is exchanged (vector from center of ball A to center of ball B). Note that there is small errors due to loss of precision is using floating point. Balls travelling horizontally or vertically are corrected to continue traveling along that axis.

Vector v1 = a.velocity; Vector v2 = b.velocity; double m1 = 1; // mass of ball A double m2 = 1; // mass of ball B // First, find the normalized vector n from the center of // ball A to the center of ball B // This is the vector along which Momentum is exchanged Vector N = a.center.vectorTo(b.center).normalize(); // Find the length of the component of each of the movement // vectors along n. // a1 = v1 . N // a2 = v2 . N double a1 = v1.dot(N); double a2 = v2.dot(N); // Using the optimized version, // optimizedP = 2(a1 - a2) // ----------- // m1 + m2 double optimizedP = (2.0 * (a1 - a2)) / (m1 + m2); // Calculate v1', the new movement vector of circle1 // v1' = v1 - optimizedP * m2 * N Vector v1_ = v1.subtract(N.scale(m1 * optimizedP)); // Calculate v1', the new movement vector of circle1 // v2' = v2 + optimizedP * m1 * N Vector v2_ = v2.add(N.scale(m2 * optimizedP)); // Here we fix some errors that can occur due to loss of precision // Handle case when both balls are travelling horizontally if (a.center.y - b.center.y == 0 && v1.y == 0 && v2.y == 0) { v1_.setPosition(v1_.x, 0); v2_.setPosition(v2_.x, 0); } // Handle case when both balls are travelling vertically if (a.center.x - b.center.x == 0 && v1.x == 0 && v2.x == 0) { v1_.setPosition(0, v1_.y); v2_.setPosition(0, v2_.y); } a.velocity = v1_; b.velocity = v2_;