Multiple Type Collision Detection

So far we've covered a circle colliding with a circle; a rectangle colliding with a rectangle; a line colliding with a line; and a rotated rectangle colliding with a rotated rectangle. We've completely left out what to do if you want to see if a circle collides with a rectangle, or if an oriented rectangle collides with a line. We can start out by covering what happens if a circle collides with all the other types.

Circle / Rectangle Collision

The way you determine if there is a collision between a rectangle and a circle you need to find the closest point on the rectangle to the center of the circle. If the distance from that point to the center of the circle is less than the radius, then there is a collision, otherwise there is no collision. The way we do this is by figuring out the range covered by the rectangle along the x axis, and the range of the rectangle on the y axis. Then, pass in the circle's center point's x value to the xrange's clamp method. That will clamp the value you pass in to a value within that range. After that do the same for the center point's y value, and you now have the xy value for the closest point on the rectangle.

Let's look at the code.

public static CircleRectangle(c: cCircleCollider, r: cRectangleCollider): boolean {
   var close_point: cVector = new cVector();
   var x_range: cRange = new cRange();
   x_range.min = r.position.x;
   x_range.max = r.position.x + r.dimension.x;

   var y_range: cRange = new cRange();
   y_range.min = r.position.y;
   y_range.max = r.position.y + r.dimension.y;

   close_point.x = x_range.clamp(c.position.x);
   close_point.y = y_range.clamp(c.position.y);

   var dist: cVector = new cVector(c.position.x, c.position.y);
   dist.subtract(close_point);

   if (dist.magSq() <= c.radius * c.radius) {
      return true;
   }
   return false;
}

Circle / Rotated Rectangle Collisions

Now that we have a circle colliding with a rectangle, the jump to a circle colliding with an oriented rectangle is really pretty simple. We just need to rotate the circle and the rectangle back to an angle of 0 degrees, then we can perform a simple circle rectangle collision test.

The first thing we do is create a rectangle and a second circle. We set the second circle's position and radius to our first circle, then rotate the position by the negative of the rotated rectangle's rotation. Then we just call our Circle Rectangle collision code with our new circle and our rectangle.

public static CircleRotRectangle(c: cCircleCollider, rr: cRotRectangleCollider): boolean {
   var r: cRectangleCollider = new cRectangleCollider();
   r.dimension = rr.halfDimension.duplicate();
   r.dimension.multiply(2);

   var lc: cCircleCollider = new cCircleCollider();
   lc.radius = c.radius;
   lc.position = c.position.duplicate();
   lc.position.subtract(rr.position);
   lc.position.rotate(-rr.rotation);
   lc.position.add(rr.halfDimension);
   return Collision.CircleRectangle(lc, r);
}

Circle Line Collision

Before we do anything more complicated, the first thing we need to do when we look to see if a line collides with a circle is to see if either of the endpoints are within the circle. That is a very simple test where you check to see if the distance from the circle's center point to either end point on the line is less than the radius of the circle. If neither end point is in the circle, we need to move on to a test using the vector projection that we used when we did the rotated rectangle collision detection. You need to pick one end point, and project a line from the center point to that end point, on to the line we are detecting the collision with. This will give us the closest point on that line to the center of our circle. The only problem is this point will not necessarily be on the line segment, so if the closest point on that line does happen to be within the radius of our circle, we then need to make sure that point is not outside of our line segment.

public static CircleLine(c: cCircleCollider, l: cLineCollider): boolean {
   var _tempVector: cVector = c.position.duplicate();
   _tempVector.subtract(l.position);
   if (_tempVector.magSq() < c.radius * c.radius) {
      return true;
   }

   _tempVector.copy(c.position);
   _tempVector.subtract(l.endPosition);
   if (_tempVector.magSq() < c.radius * c.radius) {
      return true;
   }

   var d: cVector = l.endPosition.duplicate();
   d.subtract(l.position);
   var lc: cVector = c.position.duplicate();
   lc.subtract(l.position);

   var p: cVector = lc.project(d);
   var nearest_point: cVector = l.position.duplicate();
   nearest_point.add(p);

   _tempVector = c.position.duplicate();
   _tempVector.subtract(nearest_point);

   var x_range: cRange = new cRange();
   x_range.min = l.position.x;
   x_range.max = l.endPosition.x;
   x_range.sort();

   var y_range: cRange = new cRange();
   y_range.min = l.position.y;
   y_range.max = l.endPosition.y;
   y_range.sort();

   if ((x_range.min <= nearest_point.x && nearest_point.x <= x_range.max &&
      y_range.min <= nearest_point.y && nearest_point.y <= y_range.max) == false) {
      return false;
   }

   if (_tempVector.magSq() < c.radius * c.radius) {
      return true;
   }
   return false;
}

Part 2 - More collision detection