A few months ago, I posted on the separation of axis theorem. You can learn all about SAT and how it works here. What that post failed to do was use the SAT. We will explore using SAT for collision detection in this post.

In the post on SAT, we created the code to detect collisions, however use of the code was not covered. (A download of all the classes used in the post is available at the bottom).

The Shape Classes

The easiest way to use SAT uses Shape classes. There are two shape classes we will need, the Circle and the Polygon. The circle is obviously a circle. The Polygon shape contains a lot more shapes, Squares, Rectangles, Pentagons, Hexagons, Decagons, and any other convex polygon.

For the SAT function (we’ll look at it shortly), we need a base shape class that all shapes will extend. This is an abridged version of the class, you can get the full class in the link at the bottom.

protected var _position:Vector2D;
public function set position(newPosition:Vector2D):void {
_transformMatrix.tx = newPosition.x
_transformMatrix.ty = newPosition.y;
_position = newPosition;
_transformed = false;
}
protected var _rotation:Number;
public function set rotation(newRotation:Number):void {
_rotation = newRotation;
_transformMatrix.rotate(Math2.degreesToRadians(newRotation));
_transformed = false;
}

public function set scaleX(scale:Number):void {
_scaleX = scale;
_transformMatrix.scale(_scaleX, _scaleY);
_transformed = false;
}

public function set scaleY(scale:Number):void {
_scaleY = scale;
_transformMatrix.scale(_scaleX, _scaleY);
_transformed = false;
}
protected var _transformedVertices:Array;

public function get transformedVertices():Array {
if(!_transformed) {
_transformedVertices = [];
_transformed = true;
for(var i:int = 0; i < _vertices.length; i++) {
_transformedVertices.push(_vertices[i].transform(_transformMatrix));
}
}
return _transformedVertices;
}

public function set x(x:Number):void {
_position.x = x;
_transformMatrix.tx = x;
_transformed = false;
}
public function set y(y:Number):void {
_position.y = y;
_transformMatrix.ty = y;
_transformed = false;
}

private var _transformMatrix:Matrix;

Those are the key functions. Each function adjusts the transformMatrix which is used to transform all the Vector2D’s which are used in the collision detection function.

Here is the added function to the Vector2D class:


/**
*  Transforms Vector2D based on the given Matrix
* @param matrix The matrix to use to transform this vector.
* @return Vector2D returns a new, transformed Vector2D.
*/
public function transform(matrix:Matrix):Vector2D {
var v:Vector2D = cloneVector();
v.x = _x*matrix.a + _y*matrix.c + matrix.tx;
v.y = _x*matrix.b + _y*matrix.d + matrix.ty;
return v;
}

It takes the transformation matrix and transforms the vector.

The circle class simple adds a radius and transformed radius property. The transformed radius is the radius times the scaleX.

The polygon class simply stores vertices for the collision as well as some handy creation methods like:

public static function normalPolygon(sides:int, radius:Number=100, position:Vector2D=null):Polygon {
if(sides < 3) {
throw new Error('Polygon - Needs at least 3 sides');
}
if(!position) {
position = new Vector2D();
}
var rotation:Number = (Math.PI * 2) / sides;
var angle:Number;
var vector:Vector2D;
var vertices:Array = [];
for(var i:int = 0; i < sides; i++) {
angle = (i * rotation) + ((Math.PI - rotation) * 0.5);
vector = new Vector2D();
vector.x = Math.cos(angle) * radius;
vector.y = Math.sin(angle) * radius;
vertices.push(vector);
}
return new Polygon(vertices, position);
}

This will create a normal polygon with the desired amount of sides. If you want a hexagon, simply use 6 sides. The Polygon class also supports any type of convex polygon with its constructor function. Just pass an array of Vector2D’s in, and it will create a polygon with those vertices.

The Collision Class

The Collision class is the class that holds all the collision code we created in the previous SAT post. It has some utility functions as well, like testShapes. You simply pass in two shapes (of type BaseShape) and it will return data about the collision or null if there is no collision. The testShapes function looks like this:

public static function testShapes(shape1:BaseShape, shape2:BaseShape):CollisionData {
if(shape1 is Circle && shape2 is Circle) {
return checkCircleCollision(Circle(shape1), Circle(shape2));
}
if(shape1 is Polygon && shape2 is Polygon) {
if(checkPolygonsForSAT(Polygon(shape2), Polygon(shape1))) {
return checkPolygonsForSAT(Polygon(shape1), Polygon(shape2));
}
}
if(shape1 is Circle) {
return checkCirclePolygonForSAT(Circle(shape1), Polygon(shape2));
}
if(shape2 is Circle) {
return checkCirclePolygonForSAT(Circle(shape2), Polygon(shape1));
}
return null;
}

It simply looks at the shapes, determines their types, and calls the appropriate function. That function does all the math to determine whether there is a collision or not.

These classes should make using the SAT method for collision detection much easier. Tomorrow we will use these classes to detect collisions.

Here is a .zip of the classes referenced in the post, the updated Vector2D class, the shape classes, and the collision detection class.