When Worlds Collide: Simulating Circle-Circle Collisions

When Worlds Collide: Simulating Circle-Circle Collisions

Tutorial Details
  • Difficulty: Intermediate
  • Platform Used: AS3/Flash
  • Estimated Completion Time: 60 minutes

Most collision detection in computer games is done using the AABB technique: very simply, if two rectangles intersect, then a collision has occurred. It’s fast, efficient, and incredibly effective – for rectangular objects. But what if we wanted to smash circles together? How do we calculate the collision point, and where do the objects go after? It’s not as hard as you might think…

Note: Although this tutorial is written using AS3 and Flash, you should be able to use the same techniques and concepts in almost any game development environment.

Preview image taken from this classic Psdtuts+ tutorial.


Step 1: Create Some Balls

I’m going to gloss over this step, because if you can’t create basic sprites, the rest of the tutorial is going to be a bit beyond you.

Suffice to say, we have a starting point. Below is a very rudimentary simulation of balls bouncing around a screen. If they touch the edge of the screen, they bounce back.

There is, however, one important thing to note: often, when you create sprites, the top left part is set to the origin (0, 0) and the bottom right is (width, height). Here, the circles we have created are centred on the sprite.

simulating circle-circle collisions

This makes everything considerably easier, since if the circles are not centered, for more or less every calculation we have to offset it by the radius, perform the calculation, then reset it back.

You can find the code up to this point in the v1 folder of the source download.


Step 2: Check for Overlaps

The first thing we want to do is check if our balls are near each other. There are a few ways to do this, but because we want to be good little programmers, we’re going to start off with an AABB check.

AABB stands for axis-aligned bounding box, and refers to a rectangle drawn to fit tightly around an object, aligned so that its sides are parallel to the axes.

An AABB collision check does not check whether the circles overlap each other, but it does let us know if they are near each other. Since our game only uses four objects, this is not necessary, but if we were running a simulation with 10,000 objects, then doing this little optimisation would save us many CPU cycles.

So here we go:

	if (firstBall.x + firstBall.radius + secondBall.radius > secondBall.x 
	&& firstBall.x < secondBall.x + firstBall.radius + secondBall.radius
	&& firstBall.y + firstBall.radius + secondBall.radius > secondBall.y 
	&& firstBall.y < seconBall.y + firstBall.radius + secondBall.radius)
	{
		//AABBs are overlapping
	}

This should be fairly straightforward: we’re establishing bounding boxes the size of each ball’s diameter squared.

simulating circle-circle collisions

Here, a “collision” has occurred – or rather, the two AABBs overlap, which means the circles are close to each other, and potentially colliding.

Once we know the balls are in proximity, we can be a little bit more complex. Using trigonometery, we can determine the distance between the two points:

distance = Math.sqrt(
            ((firstBall.x - secondBall.x) * (firstBall.x – secondBall.x))
          + ((firstBall.y - secondBall.y) * (firstBall.y – secondBall.y))
           );
if (distance < firstBall.radius + secondBall.radius)
{
    //balls have collided
}

Here, we’re using Pythagoras’ theorem, a^2 + b^2 = c^2, to figure out the distance between the two circles’ centers.

simulating circle-circle collisions

We don’t immediately know the lengths of a and b, but we do know the co-ordinates of each ball, so it’s trivial to work out:

a = firstBall.x – secondBall.x;
b = firstBall.y – secondBall.y;

We then solve for c with a bit of algebraic rearrangement: c = Math.sqrt(a^2 + b^2) – hence this part of the code:

distance = Math.sqrt(
            ((firstBall.x - secondBall.x) * (firstBall.x – secondBall.x))
          + ((firstBall.y - secondBall.y) * (firstBall.y – secondBall.y))
           );

We then check this value against the sum of the radii of the two circles:

if (distance < firstBall.radius + secondBall.radius)
{
    //balls have collided
}

Why do we check against the circles’ combined radii? Well, if we look at the picture below, then we can see that – no matter at what angle the circles are touching – if they are touching the line then c is equal to r1 + r2.

simulating circle-circle collisions

So – if c is equal to or less than r1 + r2, then the circles have to be touching. Simple!

Note as well that, in order to calculate collisions properly, you will likely want to move all your objects first, and then perform collision detection on them. Otherwise you may have a situation where Ball1 updates, checks for collisions, collides, then Ball2 updates, is no longer in the same area as Ball1, and reports no collision. Or, in code terms:

for (n=0; n<numberofballs; n++)
{
    updatePosition(n)
}
for (n=0; n<numberofballs; n++)
{
    calculateCollisions(n)
}

is much better than

for (n=0; n<numberofballs; n++)
{
    updatePosition(n)
    calculateCollisions(n)
}

You can find the code up to this point in the v2 folder of the source download.


Step 3: Calculate Collision Points

This part isn’t really required for ball collisions, but it’s pretty cool, so I’m throwing it in. If you just want to get everything bouncing around, feel free to skip to the next step.

It can sometimes be handy to work out the point at which two balls have collided. If you want to, for example, add a particle effect (perhaps a little explosion), or you’re creating some sort of guideline for a snooker game, then it can be useful to know the collision point.

There are two ways to work out this out: the right way, and the wrong way.

The wrong way, which many tutorials use, is to average the two points:

collisionPointX = (firstBall.x + secondBall.x)/2
collisionPointY = (firstBall.y + secondBall.y)/2

This works, but only if the balls are same size.

The formula we want to use is slightly more complicated, but works for balls of all sizes:

collisionPointX = 
 ((firstBall.x * secondBall.radius) + (secondBall.x * firstBall.radius)) 
 / (firstBall.radius + secondBall.radius);

collisionPointY = 
 ((firstBall.y * secondBall.radius) + (secondBall.y * firstBall.radius)) 
 / (firstBall.radius + secondBall.radius);

This uses the radii of the balls to give us the true x and y co-ordinates of the collision point, represented by the blue dot in the demo below.

You can find the code up to this point in the v3 folder of the source download.


Step 4: Bouncing Apart

Now, we know when our objects collide into each other, and we know their velocity and their x and y locations. How do we work out where they travel next?

We can do something called elastic collision.

Trying to explain in words how an elastic collision works can be complicated – the following animated image should make things clearer.

Simply, we’re using more triangles.

Now, we can work out the direction each ball takes, but there may be other factors at work. Spin, friction, the material the balls are made from, mass and countless other factors can be applied trying to make the “perfect” collision. We’re going to only worry about one of these: mass.

In our example, we’re going to assume that the radius of the balls we’re using is also their mass. If we were striving for realism, then this would be inaccurate since – assuming the balls were all made from the same material – the mass of the balls would be proportional to either their area or volume, depending on whether or not you wanted to consider them discs or spheres. However, since this is a simple game, using their radii will suffice.

We can use the following formula to calculate the change in x velocity of the first ball:

newVelX = 
 (firstBall.speed.x * (firstBall.mass – secondBall.mass) + (2 * secondBall.mass * secondBall.speed.x)) 
 / (firstBall.mass + secondBall.mass);

So, let’s go over this simply:

  • Assume both balls have the same mass (we’ll say 10).
  • The first ball is moving at 5 units / update (to the right). The second ball is moving -1 units/update (to the left).
NewVelX = ( 5 * (10-10) + (2 * 10 * -1) ) / (10+10)
	     = (5 * 0) + (-20) / 20
	     = -20/20
	     = -1

In this case, assuming a head-on collision, the first ball will start moving at -1 unit/update. (To the left). Because the balls’ masses are equal, the collision is direct and no energy has been lost, they balls will have “traded” speeds. Changing any of these factors will obviously change the outcome.

(If you use the same calculation to figure out the second ball’s new speed, you’ll find that it’s moving at 5 units/update, to the right).

We can use this same formula to calculate the x/y velocities of both balls after the collision:

newVelX1 = (firstBall.speed.x * (firstBall.mass – secondBall.mass) + (2 * secondBall.mass * secondBall.speed.x)) / (firstBall.mass + secondBall.mass);
newVelY1 = (firstBall.speed.y * (firstBall.mass – secondBall.mass) + (2 * secondBall.mass * secondBall.speed.y)) / (firstBall.mass + secondBall.mass);
newVelX2 = (secondBall.speed.x * (secondBall.mass – firstBall.mass) + (2 * firstBall.mass * firstBall.speed.x)) / (firstBall.mass + secondBall.mass);
newVelY2 = (secondBall.speed.y * (secondBall.mass – firstBall.mass) + (2 * firstBall.mass * firstBall.speed.y)) / (firstBall.mass + secondBall.mass);

Hopefully it’s evident that each calculation is the same, simply replacing the values accordingly.

Once this is done, we have the new velocities of each ball. So are we done? Not quite.

Earlier, we made sure to do all our position updates at once, and then check for collisions. This means that when we check for colliding balls, its very likely that one ball will be “in” another – so when the collision detection is called, both the first ball and the second ball will register that collision, meaning that our objects may get stuck together.

(If the balls are heading together, the first collision will reverse their directions – so they move apart – and the second collision will reverse their direction again, causing them to move together).

There are several ways to deal with this, such as implementing a Boolean which checks whether the balls have already collided this frame, but the easiest way is simply to move each ball by the new velocity. This means, in principle, that the balls should move apart by the same amount of speed that they moved together – placing them a distance apart equal to the frame before they collided.

                firstBall.x = firstBall.x + newVelX1;
                firstBall.y = firstBall.y + newVelY1;
                secondBall.x = secondBall.x + newVelX2;
                secondBall.y = secondBall.y + newVelY2;

And that’s it!

You can see the final product here:

And source code up to this part is available in the v4 folder of the source download.

Thanks for reading! If you’d like to learn more about collision detection methods on their own, check out this Session. You might also be interested in this tutorial about quadtrees and this tutorial about the Separating Axis Test.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://rajeshpillai.blogspot.in Rajesh Pillai

    Great tutorial and very timely.. Was trying to implement a brick breakout game… still not satisfied with the collision within multiple objects…

    If you have some spare time do have alook at the code below…and suggest improvements.. Its in html5 (canvas)..

    http://jsfiddle.net/rajeshpillai/8Qbex/4/embedded/result/

    The method in question is checkBallCollision(), which yo can see in the jsfiddle..

    If possible your inputs appreciated, as I am a beginner game programmer who has just moved into this area after about 15 years of application development (primarily database)…So, don’t know much about game programming and find this site to be absolutely timely and useful.

    Please don’t treat this as a shameless plug.. as am really looking out for improving my game programming skill.. (In case this comment is inappropriate, moderator, please feel free to delete this).

  • Tom Stephens

    Great writeup. I just noticed one minor issue. Unless I’m greatly mistaken or you have a wonky implementation the sqrt() function, the check for a distance less than zero in the second section (second listing lines 5-7) is completely unnecessary.

    Taking the square of any number (the effect of lines 2 and 3 in that listing), whether positive or negative, always yields a positive number. Adding two positive numbers always yields a positive number and taking the square root of a positive number via the standard sqrt() function in any language I’ve ever used always yields the positive root by default. So the very act of computing the distance will always result in a positive number and the check in unnecessary.

  • Tony Banks

    Wonderful tutorial. So well presented. Thanks very much

  • Darran Jamieson

    @Tom many thanks for catching that mistakes. The code is largely taken from another project of mine, in which it serves a purpose but here is superfluous. I should have caught it, but just completely glanced over the mistake for some reason.

    At any rate, Im glad someone managed to pick it up. Code has been amended to be “more optimal”.

    @Rajesh Thanks for the kind words. Unfortunately, I’m not really familiar with Javascript in a way I can offer constructive criticism. All I can advise is to join up with some online programming forums / discussion groups, or try StackExchange. Best of luck!

    • http://rajeshpillai.blogspot.in Rajesh Pillai

      @Darran Thanks. I found what I am required to do. The solution lies in step 4 of your article. I will post the fix when I am able to do so.

  • http://lovebyte.co.uk Nathan

    Great tutorial, very well explained. Want to replicate this in a C# space shooter I am developing :)

  • http://smartcade.com Shivam

    Wouldn’t it be a lot faster to square the sum of the radii rather than taking the square root of the distance between the circles?

    if ((ball1.x – ball2.x) * (ball1.x – ball2.x) +
    (ball1.y – ball2.y) * (ball1.y – ball2.y) <
    (ball1.radius + ball2.radius) * (ball1.radius + ball2.radius)) {
    // collision detected
    }

    • Poersch

      He could have used the distance var for calculating the size of the overlapping and/or to get the correct positions of the two balls after the collision – to seperate them (Seperating Axis Theorem), wich would make it a lot more stable. But he doesn’t – so yeah, it would be a LOT faster.

    • David Peyman

      Efficiency is not an issue here.

  • raju

    very nice tutorial.. thanks

  • Pitus

    Your calculateNewVelocities method is totally wrong. Assume that both balls have the same mass and that one of them is stationary (velocity x = y = 0). Your algorithm, after collision will end up moving the stationary bll in the same direction and speed as the first one was moving before collision and the first one will stop – receive the initial speed from the stationary ball.

    Note that you’ll get THE SAME resulting speed regardles of the relative position of the balls – no matter at what angle they’ll meet, their new speed will be the same.

    It’s like playing pool – no matter where you hit the stationary ball, it’ll start moving exactly the same direction as your white ball while your white ball will ALWAYS stop after hitting another ball – no matter how you hit it!

    To do this right, you need to rotate the coordinate system to align the the line going through the centers of both balls, then you can transfer the speed along that line while preserving the speeds perpendicular to it, then rotate the result back to the original coordinate system – quite a bit of triggonometry is needed.

  • David De La Peña

    Hello, I have just written a circle collision like in java and I have the same problem as you in your final product.. Sometimes some balls stick to each other or even pass through.
    Have you any idea of what happening?

  • Tharg

    I think Pitus might be right. Watching the collisions you can see that it doesn’t matter what angle the sphere’s collide. Even if they only graze they seem to bounce apart as though the collision was head on.