Basic Collisions

We already explored in our exploration of grids how we can determine collisions between objects represented as points - simply compare their coordinates:

Collisions Between two Points

if(entity1.x == entity2.x && entity1.y == entity2.y) { // Collision between entity1 & entity2 }

But what about more involved objects? A common (and useful) approach is to approximate the complexities of an object's shape with a bounding shape (or in 3D, a bounding volume). Consider Sonic from Sonic the Hedgehog. When rolling, he has a very round shape. A circle is a good approximation.

Remember that a circle is defined as a set of all points within a certain distance - the radius - from a center point. For two circles to collide (overlap), they must therefore share a point. The easiest place to find such an overlapping point is on the line between the two centers. In fact, if we think about it more deeply, we'll realize that the existence of such a point means that our radiuses are overlapping along that line, i.e. the whole distance is less than the sum of the two radiuses. We can calculate this with the distance formula:

var distance = Math.sqrt( Math.pow(entity1.centerX - entity2.centerY, 2) + Math.pow(entity1.centerY - entity2.centerY, 2) ); if(distance < entity1.radius + entity2.radius) { // collision between entity1 and entity2 }

Of course, the square root tends to be a relatively expensive calculation, and is best avoided if possible. We can escape the need for it here by squaring both sides of the inequality:

Collisions Between two Circles

var distanceSquared = Math.pow(circle1.centerX - circle2.centerX, 2) + Math.pow(circle1.centerY - circle2.centerY, 2); if(distanceSquared < Math.pow(circle1.radius + circle2.radius)) { // collision between circle1 and circle2 }

Of course, we may also want to check for a point colliding with a circle, as we might see in a mouse-clicking based game. We can assume that a point is simply a circle with radius 0, which simplifes our test to:

Collision Between a Circle and a Point

var distanceSquared = Math.pow(circle.centerX - point.x, 2) + Math.pow(circle.centerY - point.y, 2); if(distanceSquared < Math.pow(circle.radius)) { // collision between circle and point }

Other game objects are better represented with boxes or rectangles. Consider Mario from Super Mario Bros.. Mario fits nicely within a rectangle, and the many blocks he breaks and bonks are squares. There are several ways rectangles can be represented in code - a top, bottom, left, and right property (as in the CSS box model) or an x and y of the upper left corner plus a width and height (as with the rectangles we use with the Canvas element). It makes sense for us to use the latter, as in many cases we can use the same values for oth the collision detection and sprite rendering.

Detecting a collision between two rectangles is a bit more involved than with circles. Basically, we need to show that the two rectangles overlap. However, the opposite situation - showing that the two do not overlap, is actually easier. For that, we simply need to show that the left side of one rectangle is to the right of the other, or that the top of one is below the bottom of the other. We can perform this test, and invert its result with a not operation.

Collision Between two Rectangles

if(!( rect1.x < rect2.x + rect2.width || rect1.x + rect1.width > rect2.x || rect1.y < rect2.y + rect2.height || rect1.y + rect1.height > rect2.y )) { // collision between rect1 and rect2 }

Of course, we may also need to detect a collision between a rectangle and a point. Here we just test to see if the point falls inside the rectanlge, or, using the same trick as before, does not fall outside the rectangle:

if(!( point.x < rect.x || point.x > rect.x + rect.width || point.y < rect.y || point.y > rect.y + rect.height )) { // Collision between point and rect }

If we have a game that mixes both bounding boxes and bounding circles, we'll also need to compare the two. If they do collide, at least some of the points of the circle and the rectangle are shared. If we can find the point on the rectangle closest to the circle, and test the distance between that point and the center of the circle, we therefore can say they collide if the distance is less than the circle's radius. To find the closest point, we clamp the x and y coordinate of the circle to bounds defined by the sides of the rectangle.

Collision Between a Circle and Rectangle

var rx = Math.clamp(circle.x, rect.x, rect.x + rect.width); var ry = Math.clamp(circle.y, rect.y, rect.y + rect.height); var distSquared = Math.pow(rx - circle.x, 2) + Math.pow(ry - circle.y, 2); if(distSquared < Math.pow(radius, 2)) { // Collision between circle and rect }