// File "SAT.as" package { import Box2D.Common.Math.b2Vec2; import flash.display.*; import flash.geom.*; import flash.events.*; import flash.utils.Timer; /** * SAT.as * Created On: 12/03/2012 17:46 */ [SWF(backgroundColor="0x000000", frameRate="60", width="800", height="600")] public class SAT extends Sprite { private static var square:Vector.; private static var canvas:Sprite; private var A:Shape2D; private var B:Shape2D; public function SAT() { // SPRITE TO DRAW SHAPES TO: canvas = new Sprite(); // SHAPE OF A: A = new Shape2D(Vector.([ new b2Vec2( -100, -100), new b2Vec2( -100, 100), new b2Vec2( 100, 100), new b2Vec2( 100, -100)]), new b2Vec2(550, 300), 45); // SHAPE OF B: B = new Shape2D(Vector.([ new b2Vec2( -200, -50), new b2Vec2( -200, 50), new b2Vec2( 200, 50), new b2Vec2( 200, -50)]), new b2Vec2(250, 300), 175); // ADD CANVAS TO STAGE: stage.addChild(canvas); // DRAW A & B: A.draw(canvas, false); B.draw(canvas, false); collides(A, B); /* // TIMER THAT TRIGGERS FUNCTION 'ROTATE' EVERY 10MS: var t:Timer = new Timer(10); t.addEventListener(TimerEvent.TIMER, rotate); t.start(); */ } public function rotate(e:TimerEvent):void { // INCREMENT ANGLES: A.angle++; B.angle++; // CLEAR GRAPHICS: canvas.graphics.clear(); // FILL SHAPE IF IT'S CURRENTLY COLLIDING: if (collides(A, B)) { A.draw(canvas, true); B.draw(canvas, true); } else { A.draw(canvas, false); B.draw(canvas, false); } } public function collides(A:Shape2D, B:Shape2D):Boolean { var test1:Number; // numbers to use to test for overlap var test2:Number; var testNum:Number; // number to test if its the new max/min var min1:Number; // current smallest(shape 1) var max1:Number; // current largest(shape 1) var min2:Number; // current smallest(shape 2) var max2:Number; // current largest(shape 2) var axis:b2Vec2; // the normal axis for projection var offset:Number; var vectorOffset:b2Vec2; var vectors1:Vector.; // the points var vectors2:Vector.; // the points vectors1 = A.getOBB().concat(); // these functions are in my polygon class, all they do is return a Vector. of the vertices of the polygon vectors2 = B.getOBB().concat(); /* SHOULDN'T NEED THIS AS BOTH VECTORS CONSIST OF 4 OBJECTS. * ALSO B2VEC2 DOESN'T HAVE A TRUNCATE FUNCTION. * // add a little padding to make the test work correctly if (vectors1.length == 2) { var temp:b2Vec2 = new b2Vec2(-(vectors1[1].y - vectors1[0].y), vectors1[1].x - vectors1[0].x); temp.truncate(0.0000000001); vectors1.push(vectors1[1].add(temp)); } if (vectors2.length == 2) { temp = new b2Vec2(-(vectors2[1].y - vectors2[0].y), vectors2[1].x - vectors2[0].x); temp.truncate(0.0000000001); vectors2.push(vectors2[1].add(temp)); } */ // find vertical offset vectorOffset = new b2Vec2(A.position.x - B.position.x, A.position.y - B.position.y); // loop to begin projection for (var i:int = 0; i < vectors1.length; i++) { // get the normal axis, and begin projection axis = findNormalAxis(vectors1, i); trace("AXIS:", axis.x, axis.y); // project polygon1 min1 = dotProduct(axis, vectors1[0]); max1 = min1;//set max and min equal for (var j:int = 1; j < vectors1.length; j++) { testNum = dotProduct(axis, vectors1[j]);// project each point if (testNum < min1) min1 = testNum; // test for new smallest if (testNum > max1) max1 = testNum; // test for new largest } trace("MIN1:", min1); trace("MAX1:", max1); // project polygon2 min2 = dotProduct(axis, vectors2[0]); max2 = min2; // set 2's max and min for (j = 1; j < vectors2.length; j++) { testNum = dotProduct(axis, vectors2[j]);// project the point if (testNum < min2) min2 = testNum; // test for new min if (testNum > max2) max2 = testNum; // test for new max } trace("MIN2:", min2); trace("MAX2:", max2); // apply the offset to each max/min(no need for each point, max and min are all that matter) offset = dotProduct(axis, vectorOffset);// calculate offset min1 += offset; // apply offset max1 += offset; // apply offset trace("MIN1OFFSET:", min1); trace("MAX1OFFSET:", max1); // and test if they are touching test1 = min1 - max2; // test min1 and max2 test2 = min2 - max1; // test min2 and max1 trace("TEST1:", test1); trace("TEST2:", test2); trace("TEST1 > 0 || TEST2 > 0:", test1 > 0 || test2 > 0); if (test1 > 0 || test2 > 0) { //if they are greater than 0, there is a gap trace("RETURN: FALSE"); return false;//just quit } } //if you're here, there is a collision var seperation:b2Vec2 = new b2Vec2(axis.x*((max2-min1)*-1), axis.y*((max2-min1)*-1)); //return the separation, apply it to a polygon to separate the two shapes. return true; } private function dotProduct(vecA_:b2Vec2, vecB_:b2Vec2):Number { return (vecA_.x * vecB_.x + vecA_.y * vecB_.y); } private function findNormalAxis(verts:Vector., index:int):b2Vec2 { var vector1:b2Vec2 = verts[index]; var vector2:b2Vec2 = (index >= verts.length - 1) ? verts[0] : verts[index + 1]; //make sure you get a real vertex, not one that is outside the length of the vector. var normalAxis:b2Vec2 = new b2Vec2( -(vector2.y - vector1.y), vector2.x - vector1.x);//take the two vertices, make a line out of them, and find the normal of the line normalAxis.Normalize(); //normalize the line(set its length to 1) return normalAxis; } } }