Here’s a class that handles the maths behind collision detections (not a hitTest in sight ).
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.