Collision detection made easy!

Here’s a class that handles the maths behind collision detections (not a hitTest in sight :slight_smile: ).

class Collisions {
	private static var intercectionPoint:Object;
	private static var intercectionCoordinates:Array;
	
	/*
	* pointToCircle determines whether a coordinate is inside the boundries of a circle.
	* It takes two parameters, a custom object for the point: {x, y} 
	* and a custom object for the circle: {x, y, radius}, with x and y being the center coordinates.
	*/
	static function pointToCircle(point:Object, circle:Object):Boolean {
		var dx:Number = circle.x - point.x;
		var dy:Number = circle.y - point.y;
		var distance:Number = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
		return (distance <= circle.radius) ? true : false;
	}
	
	/* 
	* circleToCircle determines whether or not two circles are colliding.
	* It takes two parameters, one for each circle to check against. Each is in the form
	* of an object: {x, y, xmov, ymov, radius}
	*/
	static function circleToCircle(circle1:Object, circle2:Object):Boolean {
		var radii:Number = circle1.radius + circle2.radius;
		
		// You are not expected to understand this. But for the hell of it:
		// radii = sqrt((x2-x1)^2 + (y2-t1)^2) where xn or yn = circleN.x + circleN.xmov * t, where
		// t = time. We can rearranged this to get a quadratic for t. When solved if t is between 0 and 1, 
		// we have had a collision since our last check. Over 1 it's going to happen in the future.
		
		// at^2 + bt + c = 0
		var a:Number = (-2 * circle1.xmov * circle2.xmov + Math.pow(circle1.xmov, 2) + Math.pow(circle2.xmov, 2)) + 
				(-2 * circle1.ymov * circle2.ymov + Math.pow(circle1.ymov, 2) + Math.pow(circle2.ymov, 2));
		var b:Number = (-2 * circle1.x * circle2.xmov - 2 * circle2.x * circle1.xmov + 2 * circle1.x * circle1.xmov + 
				2 * circle2.x * circle2.xmov) + (-2 * circle1.y * circle2.ymov - 2 * circle2.y * circle1.ymov + 
				2 * circle1.y * circle1.ymov + 2 * circle2.y * circle2.ymov);
		var c:Number = (-2 * circle1.x * circle2.x + Math.pow(circle1.x, 2) + Math.pow(circle2.x, 2)) + (-2 * circle1.y * 
				circle2.y + Math.pow(circle1.y, 2) + Math.pow(circle2.y, 2) - Math.pow(radii, 2));
				
		// Use the quadratic formula to get two values for time:		
		var t:Array = [];
		t[0] = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / 2 * a;
		t[1] = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / 2 * a;
		
		var collided:Boolean;
		if (t[0] > 0 && t[1] <= 1) { 
			collided = true; 
		}
		if (t[1] > 0 && t[1] <= 1) {
			if (collided = undefined || t[1] < t[0]) {
				collided = true;
			}
		}
		
		if (collided) {
			intercectionCoordinates = [{x: circle1.x, y: circle1.y}, {x: circle2.x, y: circle2.y}];
			return true;
		} else {
			return false;
		} 
	}
	
	/* 
	* lineToLine determines whether or not two lines are intercepting.
	* It takes two parameters, two points per line.
	* In the format of: {x1, y1, x2, y2}
	*/
	static function lineToLine(line1:Object, line2:Object):Boolean {
		// Gradients of the lines.
		line1.m = (line1.y2 - line1.y1) / (line1.x2 - line1.x1);
		line2.m = (line2.y2 - line2.y1) / (line2.x2 - line2.x1);
		// Parallel lines do not intercept.
		if (line1.m == line2.m) return false
		
		// y - y1 = m(x - x1)
		// The coordinates of intercection
		var x:Number = (line1.m * line1.x1 - line2.m * line2.x1 - line1.y1 + line2.y1) / (line1.m - line2.m);
		var y:Number = line1.m * x - line1.m * line1.x1 + line1.y1;
		intercectionPoint = {x: x, y: y};
		
		// Checking to see if the coordinates are on the line segments.
		if (range(x, line1.x1, line1.x2) || range(y, line1.y1, line1.y2)) {
			if (range(x, line2.x1, line2.x2) || range(y, line2.y1, line2.y2))
				return true;
		}
		return false;
	}
	
	/*
	* lineToCircle determines if a collision between a circle and a line has or is happening
	* It takes two parameters, an object for the line and an object for the circle.
	* The line takes the format: {x1, y1, x2, y2} and the circle object: {x, y, xmov, ymov, radius}
	*/
	static function lineToCircle(line:Object, circle:Object):Boolean {
		circle.m = circle.ymov / circle.xmov;
		if (circle.m == Infinity) circle.m = 1000000;
		if (circle.m == -Infinity) circle.m = -1000000;
		circle.c = circle.y - circle.m * circle.x;
		
		// y = mx + c
		line.m = (line.y2 - line.y1) / (line.x2 - line.x1);
		line.c = line.y1 - line.m * line.x1;
		line.angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
		
		// Point of interception
		var x:Number = (circle.c - line.c) / (line.m - circle.m);
		var y:Number = line.m * x + line.c;
		
		var theta:Number = Math.atan2(circle.ymov, circle.xmov);
		var gamma:Number = theta - line.angle
		var r:Number = circle.radius / Math.sin(gamma);
		x = x - r * Math.cos(theta);
		y = y - r * Math.sin(theta);
		
		var distance:Number = Math.sqrt(Math.pow(x - circle.x, 2) + Math.pow(y - circle.y, 2));
		var velocity:Number = Math.sqrt(Math.pow(circle.xmov, 2) + Math.pow(circle.ymov, 2));
		var frames:Number = distance / velocity;
		
		var perpendicular:Object = {};
		perpendicular.m = -1/line.m;
		perpendicular.c = y - perpendicular.m * x;
		
		// Point of contact
		var contact:Object = {};
		contact.x = (line.c - perpendicular.c) / (perpendicular.m - line.m);
		contact.y = perpendicular.m * contact.x + perpendicular.c;
		if (range(contact.x, line.x1, line.x2) || range(contact.y, line.y1, line.y2)) {
			// Collision is within line segment
			if (frames <= 1 && frames > 0) {
				intercectionCoordinates = [{x: contact.x, y: contact.y}, {x: circle.x, y: circle.y}];
				return true;
			}
		} else {
			return false;
		}
	}
	
	
	/*
	* This one's easy :)
	* pointToCircle checks to see if a point is within a rectangle.
	* The point object requires: {x, y}
	* The rectangle object requires: {x, y, width, height}
	* Where x and y are the centre points for the rectangle.
	*/
	static function pointToRectangle(point:Object, rectangle:Object):Boolean {
		var walls:Object = {
			left: rectangle.x - rectangle.width/2,
			right: rectangle.x + rectangle.width/2,
			top: rectangle.y - rectangle.height/2,
			bottom: rectangle.y + rectangle.height/2
		};
		return (range(point.x, walls.left, walls.right) && range(point.y, walls.top, walls.bottom)) ? true : false;
	}
	
	/*
	* rectangleToRectangle checks to see if two rectangles are intersecting
	* It takes two identical objects, one for each rectangle that follow the form:
	* {x, y, width, height} where x and y are the center points;
	*/
	static function rectangleToRectangle(rectangle1:Object, rectangle2:Object):Boolean {
		var walls1:Object = {};
		var walls2:Object = {};
		walls1.left = rectangle1.x - rectangle1.width/2;
		walls1.right = rectangle1.x + rectangle1.width/2;
		walls1.top = rectangle1.y - rectangle1.height/2;
		walls1.bottom = rectangle1.y + rectangle1.height/2;
		
		walls2.left = rectangle2.x - rectangle2.width/2;
		walls2.right = rectangle2.x + rectangle2.width/2;
		walls2.top = rectangle2.y - rectangle2.height/2;
		walls2.bottom = rectangle2.y + rectangle2.height/2;
		
		if ((walls1.right > walls2.left && walls1.left < walls2.right) && (walls1.bottom > walls2.top && walls1.top < walls2.bottom)) {
			intercectionCoordinates = [{x: rectangle1.x, y: rectangle1.y}, {x: rectangle2.x, y: rectangle2.y}];
			return true 
		} else {
			return false;
		}
	}
	
	/* Get Methods */
	
	/*
	* Returns the interception point from the last lineToLine check performed.
	*/
	static function get lastIntercectionPoint():Object {
		return intercectionPoint;
	}
	
	/*
	* Returns an array of coordinates of the objects involved in the last collision, at the moment of collision.
	*/ 
	static function get lastIntercectionCoordinates():Array {
		return intercectionCoordinates;
	}
	
	
	/* Private Method(s) */
	private static function range(point:Number, start:Number, end:Number):Boolean {
		return (point > start && point < end) ? true : (point < start && point > end) ? true : false;
	}
}

[SIZE=“4”]How to use it:[/SIZE]
For all these examples, the variables circle1, circle2, rectangle1 and rectangle2 are all movieclips of their shape.

pointToCircle

function onMouseDown() {
	var point:Object = {x: _xmouse, y: _ymouse};
	var circle:Object = {x: circle1._x, y: circle1._y, radius: circle1._width/2};
	if (Collisions.pointToCircle(point, circle)) {
		trace("Hit!");
	} else {
		trace("Miss!");
	}
}

Pretty simple for this one. This is frame dependent.

circleToCircle

function onEnterFrame() {
	circle1._x += 1;
	circle1._y += 2;
	circle2._x -= 1;

	var c1:Object = {x: circle1._x, y: circle1._y, xmov: 1, ymov: 2, radius: circle1._width/2};
	var c2:Object = {x: circle2._x, y:circle2._y, xmov: -1, ymov: 0, radius: circle2._width/2};
	if (Collisions.circleToCircle(c1, c2)) {
		trace("Hit!");
	} else {
		trace("Miss!");
	}
}

If a collision exists, you can access the points of each circle at that moment by accessing: [FONT=“Courier New”]Collisions.lastIntercectionCoordinates[/FONT], which returns an array, [c1, c2]. This check is frame independent!

lineToLine

var line1:Object = {x1: 20, y1: 20, x2: 350, y2: 350};
var line2:Object = {x1: 40, y1: 300, x2: 400, y2: 30};
lineStyle(2, 0x000000);
moveTo(line1.x1, line1.y1);
lineTo(line1.x2, line1.y2);
moveTo(line2.x1, line2.y1);
lineTo(line2.x2, line2.y2);

if (Collisions.lineToLine(line1, line2)) {
	trace("Collision at " + Collisions.lastIntercectionPoint);
} else {
	trace("Miss!");
}

This checks to see if the line segments intercect, not the line’s trajectory (where all lines that aren’t parallel will eventually collide). You can see the use of [FONT=“Courier New”]Collisions.lastIntercectionPoint[/FONT] to return the point at which the lines collide.

lineToCircle

var line:Object = {x1: 350, y1: 20, x2: 300, y2: 350};
lineStyle(2, 0x000000);
moveTo(line.x1, line.y1);
lineTo(line.x2, line.y2);

function onEnterFrame() {
	circle1._x += 2;
	circle1._y += 1;

	var circle:Object = {x: circle1._x, y: circle1._y, xmov: 2, ymov: 1, radius: circle1._width/2};
	if (Collisions.lineToCircle(line, circle)) {
		trace("Hit!");
	} else {
		trace("Miss!");
	}
}

[FONT=“Courier New”]Collisions.lastIntercectionCoordinates[/FONT] is also available for this function, the first item in the array being the point on the line that the circle will collide with first and the second item being the x and y points of the circle at the moment of collision. This check is frame independent!

pointToRectangle

function onMouseDown() {
	var point:Object = {x: _xmouse, y: _ymouse};
	var rectangle:Object = {x: rectangle1._x, y: rectangle1._y, width: rectangle1._width, height: rectangle1._height};
	if (Collisions.pointToRectangle(point, rectangle)) {
		trace("Hit!");
	} else {
		trace("Miss!");
	}
}

Same principal as the pointToCircle check only… with… a rectangle. Frame dependent.

rectangleToRectangle

function onEnterFrame() {
	rectangle1._x += 2;
	rectangle1._y += 1;
	rectangle2._x -= 1;
	
	var r1:Object = {x: rectangle1._x, y: rectangle1._y, width: rectangle1._width, height: rectangle1._height};
	var r2:Object = {x: rectangle2._x, y: rectangle2._y, width: rectangle2._width, height: rectangle2._height};
	
	if (Collisions.rectangleToRectangle(r1, r2)) {
		trace("Hit!");
	} else {
		trace("Miss!");
	}
}

[FONT=“Courier New”]Collisions.lastIntercectionCoordinates[/FONT] will return the coordinates of the two rectangles at the point of the last collision. Frame dependent.

There you go. I hope this helps, don’t be afraid to ask any questions you have in this thread.