var TIMESLICE_EPSILON = .0001;
var MAX_TIMESLICE_SUBDIVISIONS = 32;
var timesliceRemaining;
function step(e:Event) {
// controls - spring, keys
var k = .1;
var d = .2;
if (mDown) {
pl.vx += k*(mouseX-pl.x)-d*pl.vx;
pl.vy += k*(mouseY-pl.y)-d*pl.vy;
}
if (kLeft && !kRight) {
pl.vx -= pl.speed;
} else if (kRight && !kLeft) {
pl.vx += pl.speed;
}
if (kUp && !kDown) {
pl.vy -= pl.speed;
} else if (kDown && !kUp) {
pl.vy += pl.speed;
}
pl.vx *= .85;
pl.vy *= .85;
// collisions + integration
resolveCollisions();
pl.x += pl.vx*timesliceRemaining;
pl.y += pl.vy*timesliceRemaining;
}
// comparison vars as opposed to a struct at the moment
var testTime:Number;
var testNx:Number;
var testNy:Number;
var testSurfaceArea:Number;
function resolveCollisions() {
var bestCollision:Collision = new Collision();
timesliceRemaining = 1;
var numberOfTimesSubdivided = 0;
while (timesliceRemaining > TIMESLICE_EPSILON) {
bestCollision.time = 1+TIMESLICE_EPSILON;
var collided = false;
var collisions = 0;
for (var i = 0; i < rects.length; ++i) {
// broadphase check
var bpx = pl.vx > 0 ? pl.x : pl.x + pl.vx;
var bpy = pl.vy > 0 ? pl.y : pl.y + pl.vy;
var bpw = pl.vx > 0 ? pl.vx + pl.width : pl.width - pl.vx;
var bph = pl.vy > 0 ? pl.vy + pl.height : pl.height - pl.vy;
var left = rects[i].x-(bpx+bpw);
var right = (rects[i].x+rects[i].width)-bpx;
var top = rects[i].y-(bpy+bph);
var bottom = (rects[i].y+rects[i].height)-bpy;
if (left > 0 || right < 0 || top > 0 || bottom < 0) continue;
if (intersectionTest(rects[i])) {
if (testTime < bestCollision.time ||
(testTime < bestCollision.time+TIMESLICE_EPSILON/* && testSurfaceArea > bestCollision.surfaceArea*/)) {
collided = true;
bestCollision.time = testTime;
bestCollision.nx = testNx;
bestCollision.ny = testNy;
bestCollision.surfaceArea = testSurfaceArea;
}
}
}
if (!collided) break;
resolveCollision(bestCollision, timesliceRemaining);
timesliceRemaining = (1-bestCollision.time)*timesliceRemaining;
numberOfTimesSubdivided++;
if (numberOfTimesSubdivided > MAX_TIMESLICE_SUBDIVISIONS) {
trace("max subdivisions reached!");
break;
}
}
}
function resolveCollision(collision, dt) {
// setting position to old position with corrected velocity
pl.x = pl.x+pl.vx*collision.time;
pl.y = pl.y+pl.vy*collision.time;
var nx = collision.nx;
var ny = collision.ny;
pl.vx *= dt;
pl.vy *= dt;
// inelastic vector 'reflection' for velocity, essentially zeroing it out on the correct axis
var ndv = nx*pl.vx+ny*pl.vy;
var rfmag = -1*ndv;
pl.vx += rfmag*nx;
pl.vy += rfmag*ny;
}
function intersectionTest(rect:Object):Boolean {
var xInvEntry, yInvEntry;
var xInvExit, yInvExit;
if (pl.vx > 0) {
xInvEntry = rect.x - (pl.x + pl.width);
xInvExit = (rect.x + rect.width) - pl.x;
} else {
xInvEntry = (rect.x + rect.width) - pl.x;
xInvExit = rect.x - (pl.x + pl.width);
}
if (pl.vy > 0) {
yInvEntry = rect.y - (pl.y + pl.height);
yInvExit = (rect.y + rect.height) - pl.y;
} else {
yInvEntry = (rect.y + rect.height) - pl.y;
yInvExit = rect.y - (pl.y + pl.height);
}
var xEntry, yEntry;
var xExit, yExit;
if (pl.vx == 0) {
xEntry = Number.NEGATIVE_INFINITY;
xExit = Number.POSITIVE_INFINITY;
} else {
xEntry = xInvEntry/pl.vx;
xExit = xInvExit/pl.vx;
}
if (pl.vy == 0) {
yEntry = Number.NEGATIVE_INFINITY;
yExit = Number.POSITIVE_INFINITY;
} else {
yEntry = yInvEntry/pl.vy;
yExit = yInvExit/pl.vy;
}
var entryTime = Math.max(xEntry, yEntry);
var exitTime = Math.min(xExit, yExit);
var mtd;
// calculating mtd as difference between new position and 'incorrect' overlapping position-to-be
if (entryTime > exitTime || xEntry < 0 && yEntry < 0 || xEntry > 1 && yEntry > 1) {
return false;
} else {
var normalx, normaly;
// calculate normal of collided surface
if (xEntry > yEntry) {
if (xInvEntry < 0) {
normalx = 1;
normaly = 0;
} else {
normalx = -1;
normaly = 0;
}
var newx = pl.x+pl.vx*entryTime;
mtd = Math.abs(newx-pl.x);
} else {
if (yInvEntry < 0) {
normalx = 0;
normaly = 1;
} else {
normalx = 0;
normaly = -1;
}
var newy = pl.y+pl.vy*entryTime;
mtd = Math.abs(newy-pl.y);
}
if (entryTime > 1) return false;
testTime = entryTime;
testNx = normalx;
testNy = normaly;
testSurfaceArea = mtd;
return true;
}
return false;
}