Larold’s Jubilant Junkyard” has become “Larold’s Retro Gameyard“. I’ve been working on this re-branding and migration for a while. All old tutorials should redirect you to this new site. If you see any errors, please let me know! See here for more information: What happened to Larold’s Jubilant Junkyard?

Basic Collision Detection in Games

Physics and Collision Detection are a very significant and complicated aspect of game development. A lot of math and complex equations eventually are needed. Fortunately, many games can use basic shapes to avoid complicated math.

This tutorial will revolve around 3 basic types. A single point, also called a vector; a circle, and an axis-aligned box. I’ll show you the basics of collision detection between them. I’ll also introduce example structs that represent their data.

Basic Types

These are the three types we’ll focus on:

three basic collision types. A axis-aligned box, a circle, and a single point.

A Single Point

This is the most basic type we’ll cover. A single point. This point can be thought of as a 1×1 square. Code-wise, We just need to track it’s x & y coordinates.

typedef struct Vector2Struct{

  int16_t x;
  int16_t y;

} Vector2;

A Circle

For a circle, we need 2 data pieces:

  • The center of the circle
  • The radius of the circle.

Instead of using the radius, We’ll use the radius squared. This is because some retro systems (like Game Boy) are not capable of doing a lot of real-time advanced math, and/or do not come with functions like sqrt (square root). The sqrt function is essential in collision detection with circles.

typedef struct CircleStruct{

  Vector2 center;
  int16_t radiusSquared;

} Circle;

An Axis-Aligned box

An axis-aligned box, sometimes called an “Axis-Aligned Bounding Box”, is Square or Rectangle that has not been rotated. We need three pieces of information:

  • Width
  • Height
  • A corner or the center of the box.

In our example, we’ll use the top left corner. You could use any corner. Alternatively, you could use the center of the box. It’s up to you.

typedef struct AxisAlignedBoxStruct{

  Vector2 topLeftCorner;
  int16_t width;
  int16_t height;

} AxisAlignedBox;

Axis-Aligned Box vs. A single point

To check if a box overlaps a single point, we need to make sure the point is on the proper side of each edge. The point should NOT be:

  • To the left, of the left edge
  • To the right, of the right edge.
  • Above the top edge
  • Below the bottom edge

If all of these conditions are met, the point is overlapping/inside the box.

NOTE: Some systems have inverted axes. In these systems, increasing a y position makes an object go down.

With a normal system. increasing a y position, makes an object go up:

uint8_t CollisionDetection_AxisAlignedBox_Vector(AxisAlignedBox* box, Vector2* vector){

  if(vector->x<box->topLeftcorner.x)return FALSE;
  if(vector->x>box->topLeftcorner.x+box->width)return FALSE;
  
  if(vector->y>box->topLeftcorner.y)return FALSE;
  if(vector->y<box->topLeftcorner.y-box->height)return FALSE;
  return TRUE;
}

With an inverted y-axis. increasing a y position, makes an object go down:

uint8_t CollisionDetection_AxisAlignedBox_Vector(AxisAlignedBox* box, Vector2* vector){

  if(vector->x<box->topLeftcorner.x)return FALSE;
  if(vector->x>box->topLeftcorner.x+box->width)return FALSE;
  
  if(vector->y<box->topLeftcorner.y)return FALSE;
  if(vector->y>box->topLeftcorner.y+box->height)return FALSE;
  return TRUE;
}

The remaining examples will use an inverted y-axis.

Two Axis-Aligned Boxes

box vs box collision

Two check if two axis-aligned boxes are overlapping, we DO NOT have a collision if one of the following conditions are met:

  • The left edge of boxA, is to the right of boxB’s right edge.
  • The right edge of boxA, is to the left of boxB’s left edge.
  • The top edge of boxA, is below boxB’s bottom edge.
  • The bottom edge of boxA, is above boxB’s top edge.
uint8_t CollisionDetection_AxisAlignedBox_AxisAlignedBox(AxisAlignedBox* boxA, AxisAlignedBox* 
 boxB){
 
  if(boxA->topLeftCorner.x>boxB->topLeftCorner.x+boxB.width)return FALSE;
  if(boxA->topLeftCorner.y>boxB->topLeftCorner.y+boxB.height)return FALSE;
  if(boxA->topLeftCorner.x+boxA.width<boxB->topLeftCorner.x)return FALSE;
  if(boxA->topLeftCorner.y+boxA.height<boxB->topLeftCorner.y)return FALSE;
  
  return TRUE;
}

Another way to do that, is to use math and the center’s of each box. If the distance between their centers, is less than the sum of their half widths/heights, we have a collision. That might sound confusing, here it is in code form:

uint8_t CollisionDetection_AxisAlignedBox_AxisAlignedBox(AxisAlignedBox* boxA, AxisAlignedBox* 
 boxB){
 
 int16_t boxAMidX = boxA->topLeftCorner.x+boxA->width/2;
 int16_t boxBMidX = boxB->topLeftCorner.x+boxB->width/2;
 
 int16_t minimumDistanceX = boxA->width/2+boxB->width/2;
 int16_t xd = boxAMidX-boxBMidX;
 
 if(ABS(xd)>minimumDistanceX)return FALSE;
 
 int16_t boxAMidY = boxA->topLeftCorner.y+boxA->height/2;
 int16_t boxBMidY = boxB->topLeftCorner.y+boxB->height/2;
 
 int16_t minimumDistanceY = boxA->height/2+boxB->height/2;
 int16_t yd = boxAMidY-boxBMidY;
 
 return ABS(yd)<= minimumDistanceY;
}

Two Circles

circle vs circle collision

When checking for collision against two circles, we just need to get the squared distance from their centers. If that value, is less than the sum of their two squared radii (plural version of radius), then there is collision.

uint8_t CollisionDetection_Circle_Circle(Circle* circleA, Circle* circleB){


  int16_t dx = circleA->center.x-circleB->center.x;
  int16_t dy = circleA->center.y-circleB->center.y;
  
  int16_t squaredDistance = dx*dx + dy*dy;
  
  int16_t minimumDistance = circleA->radiusSquared+circleB->radiusSquared;
  
  return squaredDistance<=minimumDistance ;
}

Circle vs A single point

Checking for collision with a Circle against a single point, is nearly the same as when checking against two circles. The only difference is we can only use one radius:

uint8_t CollisionDetection_Circle_Vector(Circle* circle, Vector2* vector){


  int16_t dx = circle->center.x-vector.x;
  int16_t dy = circle->center.y-vector.y;
  
  int16_t squaredDistance = dx*dx + dy*dy;
  
  int16_t minimumDistance = circle->radiusSquared;
  
  return squaredDistance<=minimumDistance ;
}

Circle vs Axis-Aligned Box

circle vs axis aligned box collision

To check if a circle overlaps with an axis-aligned box, we need to find the closest point on the box to the circle’s center. Next we get the distance from that point to the circle’s center. If that distance is greater than the circle’s radius, we have a collision.

Reminder: Because some retro platforms don’t have math equations like sqrt. Instead of using distance and radius, we’re using squaredDistance and radiusSquared.

uint8_t CollisionDetection_Circle_AxisAlignedBox(Circle* circle, AxisAlignedBox* box){

  int16_t closestPointOnBoxX = circle->center.x;
  int16_t closestPointOnBoxY = circle->center.y;

  if(circle->center.x<box->topLeftCorner.x)closestPointOnBoxX=box->topLeftCorner.x;
  else if(circle->center.x>box->topLeftCorner.x+box.width)closestPointOnBoxX=box->topLeftCorner.x+box.width;

  // This assumes an increasing y position moves an object downwards
  // Flip things like before for different systems.
  if(circle->center.y<box->topLeftCorner.y)closestPointOnBoxY =box->topLeftCorner.y;
  else if(circle->center.y>box->topLeftCorner.y+box.height)closestPointOnBoxY =box->topLeftCorner.y+box.height;
  
  int16_t  dx = circle->center.x-closestPointOnBoxX;
  int16_t  dy = circle->center.y-closestPointOnBoxY;
  
  int16_t  squaredDistance = dx*dx + dy*dy;
  
  return squaredDistance<=circle->radiusSquared;
}

What about rotated, or more complicated shapes?

I kept this tutorial about the basics. As soon as you introduce more complicated shapes , or rotation; the math and code become significantly more complicated. In such case, you might want to look into a third-party library.

Leave a Reply

Your email address will not be published. Required fields are marked *

THANKS FOR READING!

If you have any suggestions, and/or are confused about anything: feel free to leave a comment, send me a email, or a message on social media. Constructive criticism will help the gameyard, and others like yourself. If you learned something from this tutorial, and are looking for more content, check other these other tutorials. 

Sign-Up for the "Gameyard Newsletter" for MOre Game Development News

If you like Retro Game Development, subscribe to the Gameyard Newsletter. Stay up-to-date with all the latest Game Boy news. It’s free, and you can unsubscribe any time!

Be sure to check your email inbox for a confirmation email.