Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

ffs

  • Loading branch information...
commit 90feba4f0e20e40cbabda744872b6f3a23edbcb5 1 parent 10fb2ba
@Pomax authored
View
1  Boundary.pde
@@ -194,4 +194,3 @@ class Boundary extends Positionable {
*/
String toString() { return x+","+y+","+xw+","+yh; }
}
-
View
252 CollisionDetection.pde
@@ -137,156 +137,159 @@ static class CollisionDetection {
/**
- * Line-through-box intersection algorithm.
- *
- * There are a few possibilities, with B() meaning
- * "on blocking side", I() meaning "intersecting"
- * and P() meaning "on pass-through side":
- *
- * |
- * (1) [] [] <| = B(prev), B(current)
- * |
- *
- * |
- * (2) [] [<|] = B(prev), B(current) and I(current) and P(current)
- * |
- *
- * |
- * (3) [] <| [] = B(prev), P(current)
- * |
- *
- * |
- * (4) [<|] [] = B(prev) and I(prev) and P(prev), P(current)
- * |
+ * generate the dot products for each of the current
+ * and previous corner points, relative to (ox,oy).
*
- *
- * |
- * (5) <| [] [] = P(prev), P(current)
- * |
- *
- * By computing the dot product for the eight corners of the two bounding
- * boxes, we can determine which of these situations we are in. Only in
- * (2) and (3) should we signal an intersection occuring. While (4) might
- * look like another case in which this should happen, we assume that
- * because the "previous" state is boundary overlapping, it was permitted
- * earlier (for instance, it moved through the boundary in the allowed
- * direction, but then moved back before having fully passed the boundary).
- *
- * DOCUMENTATION FIX: we can actually treat 2 and 3 as the same case, with
- * a more reliable result, by always constructing a
- * new intersection-validating parallelogram from the
- * "hot corner" and an adjacent corner in {previous} and
- * {current}.
- *
- *
- * @return null if no intersect, otherwise float[3]
- * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ * note: we want dot products with the vector that is
+ * perpendicular to the boundary, so that we have
+ * a clear "on blocked side" (positive) and "on
+ * pass-through side" (negative) value classifier.
*/
- static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
- {
- int B_current = 0, B_previous = 0, B_sum = 0;
+ private static int[] getDotProductsPerCorner(float[] line, float[] current, float[] current_dots, float[] previous, float[] previous_dots, int[] corners) {
+ int B_current = 0,
+ B_previous = 0,
+ B_sum = 0;
+
float ox=line[0], oy=line[1], tx=line[2], ty=line[3],
dotx = tx-ox, doty = ty-oy,
- otlen = sqrt(dotx*dotx + doty*doty),
- x, y, dx, dy, len, dotproduct;
+ otlen = sqrt(dotx*dotx + doty*doty);
- // normalised boundary vector
+ // normalise the boundary vector
dotx /= otlen;
doty /= otlen;
- // note: we want the dot product with the
- // vector that is perpendicular to the boundary!
float p = PI/2, cosp = cos(p), sinp = sin(p),
pdx = dotx*cosp - doty*sinp,
- pdy = dotx*sinp + doty*cosp;
+ pdy = dotx*sinp + doty*cosp,
+ dx, dy, len, dotproduct,
+ dotvalue = 999999, curval;
+
+ float corner1v=3, corner2v=3, corner3v=3, corner4v=3;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
- // how many corners are blocked in [current]?
+ // FIXME: I'm sure this can be optimised
for(int i=0; i<8; i+=2) {
- x = current[i];
- y = current[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'current'
+ dx = current[i]-ox;
+ dy = current[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_current++; }
current_dots[i] = dotproduct;
- }
- // how many corners are blocked in [previous]? (copied for speed)
- for(int i=0; i<8; i+=2) {
- x = previous[i];
- y = previous[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'previous'
+ dx = previous[i]-ox;
+ dy = previous[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_previous++; }
previous_dots[i] = dotproduct;
+
+ // this is silly, but relatively fast
+ if(dotproduct<corner1v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = corner1v; corner1v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = corner1; corner1 = i; }
+ else if(dotproduct<corner2v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = i; }
+ else if(dotproduct<corner3v) {
+ corner4v = corner3v; corner3v = dotproduct;
+ corner4 = corner3; corner3 = i; }
+ else {
+ corner4v = dotproduct;
+ corner4 = i; }
}
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
+
+ // return "on blocking side" counts for both boxes
+ return new int[]{B_current, B_previous, B_current+B_previous};
+ }
- // cases (1) or (5)?
- B_sum = B_current + B_previous;
- if (B_sum == 0 || B_sum == 8) {
-// println("case " + (B_sum==8? "1" : "5"));
- return null;
- }
- // case (4)?
- if (B_previous < 4 && B_current == 0) {
-// println("case 4");
- return null;
- }
+ /**
+ * Line-through-box intersection algorithm.
+ *
+ * There are a few possibilities, illustrated
+ * in the CollisionDetection.psd and
+ * CollisionDetection2.psd files included with
+ * this source code.
+ *
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ */
+ static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
+ {
+ float ox=line[0], oy=line[1], tx=line[2], ty=line[3];
+
+ // get the dot products per corner... just like the method says
+ int[] corners = {0,0,0,0},
+ B_counts = getDotProductsPerCorner(line, current, current_dots, previous, previous_dots, corners);
+
+ int B_current = B_counts[0],
+ B_previous = B_counts[1],
+ B_sum = B_counts[2];
- // Before we continue, find the point in [previous]
- // that is closest to the boundary, since that'll
- // cross the boundary first (based on its dot product
- // value.)
- int corner=-1, curcorner;
- float dotvalue = 999999, curval;
- for(curcorner=0; curcorner<8; curcorner+=2) {
- curval = abs(previous_dots[curcorner]);
- if(curval<dotvalue) {
- corner = curcorner;
- dotvalue = curval; }}
-
- // Right then. Case (2) or (3)?
+ // cases (1) or (6)? no collision.
+ if (B_sum == 0 || B_sum == 8) { return null; }
+
+ // case (4) or (5)? no collision, by convention.
+ if (B_previous < 4) { return null; }
+
+ // what remains is case (2) or (3)
if (B_previous == 4) {
- int currentCase = 2;
- // for (2) and (3) the approach is essentially
- // the same, except that we need different bounding
- // boxes to determine the intersection. For (2) we
- // can use the [current] bounding box, but for (3)
- // we need to construct a new box that is spanned by
- // the two points next to the "hot" corner in both
- // [previous] and [current].
- //
- // FIXME: that said, if we treat (2) as a subset
- // of (3), things seem to work better.
- checkLines = new float[8];
- arrayCopy(current,0,checkLines,0,8);
-
- currentCase = 3;
-
- // two of these edges are guaranteed to not have intersections,
- // since otherwise the intersection would be inside either
- // [previous] or [current], which is case (2) instead.
- checkLines = new float[]{previous[(corner)%8], previous[(corner+1)%8],
+
+ // first, we need to find out which corner point actually
+ // intersects our boundary. Typically this will be the
+ // first ranked corner, but not always.
+ float x1, y1, x2, y2;
+ int corner = -1;
+ float[] intersection;
+ for(int i=0, last=corners.length; i<last; i++) {
+ corner = corners[i];
+ x1 = previous[corner];
+ y1 = previous[corner+1];
+ x2 = current[corner];
+ y2 = current[corner+1];
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ if(intersection!=null) break;
+ }
+
+ // get the guaranteed hit-line
+ float px = previous[corner],
+ py = previous[corner+1],
+ cx = current[corner],
+ cy = current[corner+1];
+
+ // We now have the corner that's going to pass through
+ // the boundary first. we construct a new bbox to
+ // perform poly-through-line detection.
+ checkLines = new float[]{px, py,
previous[(corner+2)%8], previous[(corner+3)%8],
current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
+ cx, cy};
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
intersections.clear();
- float x1=checkLines[0], y1=checkLines[1], x2=x1, y2=y1;
+ x1=checkLines[0];
+ y1=checkLines[1];
+ x2=x1;
+ y2=y1;
for (int i=0, last=checkLines.length; i<last; i+=2) {
x2 = checkLines[(i+2)%last];
y2 = checkLines[(i+3)%last];
- float[] intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
if (intersection!=null) {
intersections.add(intersection);
if(intersections.size()==2) { break; }}
@@ -298,12 +301,6 @@ static class CollisionDetection {
// (We can have zero, one, or two)
int intersectionCount = intersections.size();
- // get the supposed hit-line
- float px = previous[corner],
- py = previous[corner+1],
- cx = current[corner],
- cy = current[corner+1];
-
// if we have two intersections, it's
// relatively easy to determine by how
// much we should move back.
@@ -313,21 +310,7 @@ static class CollisionDetection {
i2 = intersections.get(1);
float[] ideal = getLineLineIntersection(px,py,cx,cy, i1[0],i1[1],i2[0],i2[1], false);
if (ideal == null) {
- if(debug) println("error: could not find the case "+currentCase+" ideal point based on corner ["+corner+"]");
- /*
- println("tried to find the intersection between:");
- println("[1] "+px+","+py+","+cx+","+cy+" (blue)");
- println("[2] "+i1[0]+","+i1[1]+","+i2[0]+","+i2[1]+" (green)");
- //debugfunctions_drawBoundingBox(current,sketch);
- //debugfunctions_drawBoundingBox(previous,sketch);
- debugfunctions_drawBoundingBox(checkLines,sketch);
-
- sketch.noLoop();
- sketch.stroke(0,200,255);
- sketch.line(px,py,cx,cy);
- sketch.stroke(0,255,0);
- sketch.line(i1[0],i1[1],i2[0],i2[1]);
- */
+ if(debug) println("error: could not find the ideal point based on corner ["+corner+"]");
return new float[]{px-cx, py-cy, corner};
}
return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
@@ -364,7 +347,8 @@ static class CollisionDetection {
}
// Uncaught cases get a pass - with a warning.
-// println("unknown case! (B_current: "+B_current+", B_previous: "+B_previous+")");
+ // At least theoretically, there are no uncaught cases.
+ // But as a programmer, I can be wrong.
return null;
}
@@ -451,4 +435,4 @@ static class CollisionDetection {
// And then return the transformed coordinates, plus angle used
return new float[] {x1n, y1n, x2n, y2n, 0, 0, x4n, 0, angle};
}
-}
+}
View
BIN  CollisionDetection.psd
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  CollisionDetection2.psd
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
14 LevelLayer.pde
@@ -432,7 +432,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -444,7 +449,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
View
4 Position.pde
@@ -118,7 +118,9 @@ class Position {
// overlap
float angle = atan2(dy,dx);
if(angle<0) { angle += 2*PI; }
- return new float[]{dx, dy, angle};
+ float safedx = dw-dx,
+ safedy = dh-dy;
+ return new float[]{dx, dy, angle, safedx, safedy};
}
/**
View
6 Positionable.pde
@@ -64,6 +64,10 @@ abstract class Positionable extends Position implements Drawable {
void detachFromAll() {
boundaries.clear();
}
+
+ void rewind() {
+ copyFrom(previous);
+ }
/**
* change the position, relative
@@ -282,7 +286,6 @@ abstract class Positionable extends Position implements Drawable {
// we're attached to one or more boundaries, so we
// are subject to (compound) impulse redirection.
if(boundaries.size()>0) {
-// println("redirecting impulse {"+_dx+","+_dy+"} over boundary surfaces...");
float[] redirected = new float[]{_dx, _dy};
for(int b=boundaries.size()-1; b>=0; b--) {
Boundary boundary = boundaries.get(b);
@@ -291,7 +294,6 @@ abstract class Positionable extends Position implements Drawable {
continue;
}
redirected = boundary.redirectForce(redirected[0], redirected[1]);
-// println("redirected to {"+redirected[0]+","+redirected[1]+"}.");
}
x += redirected[0];
y += redirected[1];
View
1  build.bat
@@ -1,4 +1,3 @@
cat *.pde > codebase.pde
cp codebase.pde mario/
cp codebase.pde docs/tutorial/
-rm codebase.pde
View
340 docs/tutorial/codebase.pde
@@ -591,7 +591,6 @@ class Boundary extends Positionable {
*/
String toString() { return x+","+y+","+xw+","+yh; }
}
-
/**
* A bounded interactor is a normal Interactor with
* one or more boundaries associated with it.
@@ -836,156 +835,159 @@ static class CollisionDetection {
/**
- * Line-through-box intersection algorithm.
- *
- * There are a few possibilities, with B() meaning
- * "on blocking side", I() meaning "intersecting"
- * and P() meaning "on pass-through side":
- *
- * |
- * (1) [] [] <| = B(prev), B(current)
- * |
- *
- * |
- * (2) [] [<|] = B(prev), B(current) and I(current) and P(current)
- * |
- *
- * |
- * (3) [] <| [] = B(prev), P(current)
- * |
- *
- * |
- * (4) [<|] [] = B(prev) and I(prev) and P(prev), P(current)
- * |
- *
- *
- * |
- * (5) <| [] [] = P(prev), P(current)
- * |
- *
- * By computing the dot product for the eight corners of the two bounding
- * boxes, we can determine which of these situations we are in. Only in
- * (2) and (3) should we signal an intersection occuring. While (4) might
- * look like another case in which this should happen, we assume that
- * because the "previous" state is boundary overlapping, it was permitted
- * earlier (for instance, it moved through the boundary in the allowed
- * direction, but then moved back before having fully passed the boundary).
+ * generate the dot products for each of the current
+ * and previous corner points, relative to (ox,oy).
*
- * DOCUMENTATION FIX: we can actually treat 2 and 3 as the same case, with
- * a more reliable result, by always constructing a
- * new intersection-validating parallelogram from the
- * "hot corner" and an adjacent corner in {previous} and
- * {current}.
- *
- *
- * @return null if no intersect, otherwise float[3]
- * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ * note: we want dot products with the vector that is
+ * perpendicular to the boundary, so that we have
+ * a clear "on blocked side" (positive) and "on
+ * pass-through side" (negative) value classifier.
*/
- static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
- {
- int B_current = 0, B_previous = 0, B_sum = 0;
+ private static int[] getDotProductsPerCorner(float[] line, float[] current, float[] current_dots, float[] previous, float[] previous_dots, int[] corners) {
+ int B_current = 0,
+ B_previous = 0,
+ B_sum = 0;
+
float ox=line[0], oy=line[1], tx=line[2], ty=line[3],
dotx = tx-ox, doty = ty-oy,
- otlen = sqrt(dotx*dotx + doty*doty),
- x, y, dx, dy, len, dotproduct;
+ otlen = sqrt(dotx*dotx + doty*doty);
- // normalised boundary vector
+ // normalise the boundary vector
dotx /= otlen;
doty /= otlen;
- // note: we want the dot product with the
- // vector that is perpendicular to the boundary!
float p = PI/2, cosp = cos(p), sinp = sin(p),
pdx = dotx*cosp - doty*sinp,
- pdy = dotx*sinp + doty*cosp;
+ pdy = dotx*sinp + doty*cosp,
+ dx, dy, len, dotproduct,
+ dotvalue = 999999, curval;
+
+ float corner1v=3, corner2v=3, corner3v=3, corner4v=3;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
- // how many corners are blocked in [current]?
+ // FIXME: I'm sure this can be optimised
for(int i=0; i<8; i+=2) {
- x = current[i];
- y = current[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'current'
+ dx = current[i]-ox;
+ dy = current[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_current++; }
current_dots[i] = dotproduct;
- }
- // how many corners are blocked in [previous]? (copied for speed)
- for(int i=0; i<8; i+=2) {
- x = previous[i];
- y = previous[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'previous'
+ dx = previous[i]-ox;
+ dy = previous[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_previous++; }
previous_dots[i] = dotproduct;
+
+ // this is silly, but relatively fast
+ if(dotproduct<corner1v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = corner1v; corner1v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = corner1; corner1 = i; }
+ else if(dotproduct<corner2v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = i; }
+ else if(dotproduct<corner3v) {
+ corner4v = corner3v; corner3v = dotproduct;
+ corner4 = corner3; corner3 = i; }
+ else {
+ corner4v = dotproduct;
+ corner4 = i; }
}
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
- // cases (1) or (5)?
- B_sum = B_current + B_previous;
- if (B_sum == 0 || B_sum == 8) {
-// println("case " + (B_sum==8? "1" : "5"));
- return null;
- }
+ // return "on blocking side" counts for both boxes
+ return new int[]{B_current, B_previous, B_current+B_previous};
+ }
- // case (4)?
- if (B_previous < 4 && B_current == 0) {
-// println("case 4");
- return null;
- }
- // Before we continue, find the point in [previous]
- // that is closest to the boundary, since that'll
- // cross the boundary first (based on its dot product
- // value.)
- int corner=-1, curcorner;
- float dotvalue = 999999, curval;
- for(curcorner=0; curcorner<8; curcorner+=2) {
- curval = abs(previous_dots[curcorner]);
- if(curval<dotvalue) {
- corner = curcorner;
- dotvalue = curval; }}
-
- // Right then. Case (2) or (3)?
+ /**
+ * Line-through-box intersection algorithm.
+ *
+ * There are a few possibilities, illustrated
+ * in the CollisionDetection.psd and
+ * CollisionDetection2.psd files included with
+ * this source code.
+ *
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ */
+ static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
+ {
+ float ox=line[0], oy=line[1], tx=line[2], ty=line[3];
+
+ // get the dot products per corner... just like the method says
+ int[] corners = {0,0,0,0},
+ B_counts = getDotProductsPerCorner(line, current, current_dots, previous, previous_dots, corners);
+
+ int B_current = B_counts[0],
+ B_previous = B_counts[1],
+ B_sum = B_counts[2];
+
+ // cases (1) or (6)? no collision.
+ if (B_sum == 0 || B_sum == 8) { return null; }
+
+ // case (4) or (5)? no collision, by convention.
+ if (B_previous < 4) { return null; }
+
+ // what remains is case (2) or (3)
if (B_previous == 4) {
- int currentCase = 2;
- // for (2) and (3) the approach is essentially
- // the same, except that we need different bounding
- // boxes to determine the intersection. For (2) we
- // can use the [current] bounding box, but for (3)
- // we need to construct a new box that is spanned by
- // the two points next to the "hot" corner in both
- // [previous] and [current].
- //
- // FIXME: that said, if we treat (2) as a subset
- // of (3), things seem to work better.
- checkLines = new float[8];
- arrayCopy(current,0,checkLines,0,8);
-
- currentCase = 3;
-
- // two of these edges are guaranteed to not have intersections,
- // since otherwise the intersection would be inside either
- // [previous] or [current], which is case (2) instead.
- checkLines = new float[]{previous[(corner)%8], previous[(corner+1)%8],
+
+ // first, we need to find out which corner point actually
+ // intersects our boundary. Typically this will be the
+ // first ranked corner, but not always.
+ float x1, y1, x2, y2;
+ int corner = -1;
+ float[] intersection;
+ for(int i=0, last=corners.length; i<last; i++) {
+ corner = corners[i];
+ x1 = previous[corner];
+ y1 = previous[corner+1];
+ x2 = current[corner];
+ y2 = current[corner+1];
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ if(intersection!=null) break;
+ }
+
+ // get the guaranteed hit-line
+ float px = previous[corner],
+ py = previous[corner+1],
+ cx = current[corner],
+ cy = current[corner+1];
+
+ // We now have the corner that's going to pass through
+ // the boundary first. we construct a new bbox to
+ // perform poly-through-line detection.
+ checkLines = new float[]{px, py,
previous[(corner+2)%8], previous[(corner+3)%8],
current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
+ cx, cy};
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
intersections.clear();
- float x1=checkLines[0], y1=checkLines[1], x2=x1, y2=y1;
+ x1=checkLines[0];
+ y1=checkLines[1];
+ x2=x1;
+ y2=y1;
for (int i=0, last=checkLines.length; i<last; i+=2) {
x2 = checkLines[(i+2)%last];
y2 = checkLines[(i+3)%last];
- float[] intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
if (intersection!=null) {
intersections.add(intersection);
if(intersections.size()==2) { break; }}
@@ -997,12 +999,6 @@ static class CollisionDetection {
// (We can have zero, one, or two)
int intersectionCount = intersections.size();
- // get the supposed hit-line
- float px = previous[corner],
- py = previous[corner+1],
- cx = current[corner],
- cy = current[corner+1];
-
// if we have two intersections, it's
// relatively easy to determine by how
// much we should move back.
@@ -1012,21 +1008,7 @@ static class CollisionDetection {
i2 = intersections.get(1);
float[] ideal = getLineLineIntersection(px,py,cx,cy, i1[0],i1[1],i2[0],i2[1], false);
if (ideal == null) {
- if(debug) println("error: could not find the case "+currentCase+" ideal point based on corner ["+corner+"]");
- /*
- println("tried to find the intersection between:");
- println("[1] "+px+","+py+","+cx+","+cy+" (blue)");
- println("[2] "+i1[0]+","+i1[1]+","+i2[0]+","+i2[1]+" (green)");
- //debugfunctions_drawBoundingBox(current,sketch);
- //debugfunctions_drawBoundingBox(previous,sketch);
- debugfunctions_drawBoundingBox(checkLines,sketch);
-
- sketch.noLoop();
- sketch.stroke(0,200,255);
- sketch.line(px,py,cx,cy);
- sketch.stroke(0,255,0);
- sketch.line(i1[0],i1[1],i2[0],i2[1]);
- */
+ if(debug) println("error: could not find the ideal point based on corner ["+corner+"]");
return new float[]{px-cx, py-cy, corner};
}
return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
@@ -1063,7 +1045,8 @@ static class CollisionDetection {
}
// Uncaught cases get a pass - with a warning.
-// println("unknown case! (B_current: "+B_current+", B_previous: "+B_previous+")");
+ // At least theoretically, there are no uncaught cases.
+ // But as a programmer, I can be wrong.
return null;
}
@@ -1150,8 +1133,7 @@ static class CollisionDetection {
// And then return the transformed coordinates, plus angle used
return new float[] {x1n, y1n, x2n, y2n, 0, 0, x4n, 0, angle};
}
-}
-/**
+}/**
* A static computation class.
*
* At the moment this houses the actor/boundary interaction
@@ -1977,7 +1959,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -1989,7 +1976,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -2275,7 +2267,9 @@ class Position {
// overlap
float angle = atan2(dy,dx);
if(angle<0) { angle += 2*PI; }
- return new float[]{dx, dy, angle};
+ float safedx = dw-dx,
+ safedy = dh-dy;
+ return new float[]{dx, dy, angle, safedx, safedy};
}
/**
@@ -2375,6 +2369,10 @@ abstract class Positionable extends Position implements Drawable {
void detachFromAll() {
boundaries.clear();
}
+
+ void rewind() {
+ copyFrom(previous);
+ }
/**
* change the position, relative
@@ -2593,7 +2591,6 @@ abstract class Positionable extends Position implements Drawable {
// we're attached to one or more boundaries, so we
// are subject to (compound) impulse redirection.
if(boundaries.size()>0) {
-// println("redirecting impulse {"+_dx+","+_dy+"} over boundary surfaces...");
float[] redirected = new float[]{_dx, _dy};
for(int b=boundaries.size()-1; b>=0; b--) {
Boundary boundary = boundaries.get(b);
@@ -2602,7 +2599,6 @@ abstract class Positionable extends Position implements Drawable {
continue;
}
redirected = boundary.redirectForce(redirected[0], redirected[1]);
-// println("redirected to {"+redirected[0]+","+redirected[1]+"}.");
}
x += redirected[0];
y += redirected[1];
@@ -4024,7 +4020,7 @@ void drawBackground(float width, float height) {
/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
-///*
+/*
final int screenWidth = 512;
final int screenHeight = 432;
@@ -4035,71 +4031,43 @@ void initialize() {
class TestLevel extends Level {
TestLevel(float w, float h) {
super(w,h);
- addLevelLayer("test", new TestLayer(this,w,h));
+ addLevelLayer("test", new TestLayer(this));
}
void draw() {
- fill(0,1);
+ fill(0,10);
rect(-1,-1,width+2,height+2);
super.draw();
- stroke(0,255,0,150);
- line(width/2,0,width/2,height);
- line(width/2+16,0,width/2+16,height);
}
}
class TestLayer extends LevelLayer {
TestObject t1, t2, t3, t4;
- TestLayer(Level p, float w, float h) {
- super(p,w,h);
+ TestLayer(Level p) {
+ super(p,p.width,p.height);
showBoundaries = true;
+
+ int v = 10;
- t1 = new TestObject(width/4, height/4);
+ t1 = new TestObject(width/2+v/2, height/2-200);
t1.setForces(5,0);
+
+ t1.setForces(0,0.01);
+ t1.setAcceleration(0,0.1);
- t2 = new TestObject(width/4, 3*height/4);
- t2.setForces(0,-5);
-
- t3 = new TestObject(3*width/4, 3*height/4);
- t3.setForces(-5,0);
-
- t4 = new TestObject(3*width/4, height/4);
- t4.setForces(0,5);
-
-// addPlayer(t1);
- addPlayer(t2);
-// addPlayer(t3);
-// addPlayer(t4);
+ addPlayer(t1);
addBoundary(new Boundary(width/2+230,height,width/2+200,0));
- addBoundary(new Boundary(width/2-180,0,width/2-150,height));
-
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
addBoundary(new Boundary(width,height/2-200,0,height/2-120));
addBoundary(new Boundary(0,height/2+200,width,height/2+120));
- addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ addBoundary(new Boundary(width/2,height/2,width/2 + v,height/2));
}
- void draw() {
+ void draw() {
super.draw();
-
- if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
- else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
-
- if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
- else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
-
- if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
- else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
-
- if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
- else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
- }
-
- void mouseClicked(int mx, int my, int mb) {
- if(mb==RIGHT) { noLoop(); return; }
- boundaries.clear();
- addBoundary(new Boundary(width/2, height/2, mx, my));
+ viewbox.track(parent,t1);
}
}
@@ -4111,4 +4079,4 @@ class TestObject extends Player {
}
}
-//*/
+*/
View
4,098 docs/tutorial/codebase.pde.tmp
4,098 additions, 0 deletions not shown
View
4 docs/tutorial/sketches/mario/sketch17.pde
@@ -127,8 +127,8 @@ class MainLevelLayer extends LevelLayer {
// and let's add the thing that makes us win!
addGoal(1920, height-48);
- showBoundaries = true;
- showTriggers = true;
+ //showBoundaries = true;
+ //showTriggers = true;
}
/**
View
340 mario/codebase.pde
@@ -591,7 +591,6 @@ class Boundary extends Positionable {
*/
String toString() { return x+","+y+","+xw+","+yh; }
}
-
/**
* A bounded interactor is a normal Interactor with
* one or more boundaries associated with it.
@@ -836,156 +835,159 @@ static class CollisionDetection {
/**
- * Line-through-box intersection algorithm.
- *
- * There are a few possibilities, with B() meaning
- * "on blocking side", I() meaning "intersecting"
- * and P() meaning "on pass-through side":
- *
- * |
- * (1) [] [] <| = B(prev), B(current)
- * |
- *
- * |
- * (2) [] [<|] = B(prev), B(current) and I(current) and P(current)
- * |
- *
- * |
- * (3) [] <| [] = B(prev), P(current)
- * |
- *
- * |
- * (4) [<|] [] = B(prev) and I(prev) and P(prev), P(current)
- * |
- *
- *
- * |
- * (5) <| [] [] = P(prev), P(current)
- * |
- *
- * By computing the dot product for the eight corners of the two bounding
- * boxes, we can determine which of these situations we are in. Only in
- * (2) and (3) should we signal an intersection occuring. While (4) might
- * look like another case in which this should happen, we assume that
- * because the "previous" state is boundary overlapping, it was permitted
- * earlier (for instance, it moved through the boundary in the allowed
- * direction, but then moved back before having fully passed the boundary).
+ * generate the dot products for each of the current
+ * and previous corner points, relative to (ox,oy).
*
- * DOCUMENTATION FIX: we can actually treat 2 and 3 as the same case, with
- * a more reliable result, by always constructing a
- * new intersection-validating parallelogram from the
- * "hot corner" and an adjacent corner in {previous} and
- * {current}.
- *
- *
- * @return null if no intersect, otherwise float[3]
- * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ * note: we want dot products with the vector that is
+ * perpendicular to the boundary, so that we have
+ * a clear "on blocked side" (positive) and "on
+ * pass-through side" (negative) value classifier.
*/
- static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
- {
- int B_current = 0, B_previous = 0, B_sum = 0;
+ private static int[] getDotProductsPerCorner(float[] line, float[] current, float[] current_dots, float[] previous, float[] previous_dots, int[] corners) {
+ int B_current = 0,
+ B_previous = 0,
+ B_sum = 0;
+
float ox=line[0], oy=line[1], tx=line[2], ty=line[3],
dotx = tx-ox, doty = ty-oy,
- otlen = sqrt(dotx*dotx + doty*doty),
- x, y, dx, dy, len, dotproduct;
+ otlen = sqrt(dotx*dotx + doty*doty);
- // normalised boundary vector
+ // normalise the boundary vector
dotx /= otlen;
doty /= otlen;
- // note: we want the dot product with the
- // vector that is perpendicular to the boundary!
float p = PI/2, cosp = cos(p), sinp = sin(p),
pdx = dotx*cosp - doty*sinp,
- pdy = dotx*sinp + doty*cosp;
+ pdy = dotx*sinp + doty*cosp,
+ dx, dy, len, dotproduct,
+ dotvalue = 999999, curval;
+
+ float corner1v=3, corner2v=3, corner3v=3, corner4v=3;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
- // how many corners are blocked in [current]?
+ // FIXME: I'm sure this can be optimised
for(int i=0; i<8; i+=2) {
- x = current[i];
- y = current[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'current'
+ dx = current[i]-ox;
+ dy = current[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_current++; }
current_dots[i] = dotproduct;
- }
- // how many corners are blocked in [previous]? (copied for speed)
- for(int i=0; i<8; i+=2) {
- x = previous[i];
- y = previous[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'previous'
+ dx = previous[i]-ox;
+ dy = previous[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_previous++; }
previous_dots[i] = dotproduct;
+
+ // this is silly, but relatively fast
+ if(dotproduct<corner1v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = corner1v; corner1v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = corner1; corner1 = i; }
+ else if(dotproduct<corner2v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = i; }
+ else if(dotproduct<corner3v) {
+ corner4v = corner3v; corner3v = dotproduct;
+ corner4 = corner3; corner3 = i; }
+ else {
+ corner4v = dotproduct;
+ corner4 = i; }
}
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
- // cases (1) or (5)?
- B_sum = B_current + B_previous;
- if (B_sum == 0 || B_sum == 8) {
-// println("case " + (B_sum==8? "1" : "5"));
- return null;
- }
+ // return "on blocking side" counts for both boxes
+ return new int[]{B_current, B_previous, B_current+B_previous};
+ }
- // case (4)?
- if (B_previous < 4 && B_current == 0) {
-// println("case 4");
- return null;
- }
- // Before we continue, find the point in [previous]
- // that is closest to the boundary, since that'll
- // cross the boundary first (based on its dot product
- // value.)
- int corner=-1, curcorner;
- float dotvalue = 999999, curval;
- for(curcorner=0; curcorner<8; curcorner+=2) {
- curval = abs(previous_dots[curcorner]);
- if(curval<dotvalue) {
- corner = curcorner;
- dotvalue = curval; }}
-
- // Right then. Case (2) or (3)?
+ /**
+ * Line-through-box intersection algorithm.
+ *
+ * There are a few possibilities, illustrated
+ * in the CollisionDetection.psd and
+ * CollisionDetection2.psd files included with
+ * this source code.
+ *
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ */
+ static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
+ {
+ float ox=line[0], oy=line[1], tx=line[2], ty=line[3];
+
+ // get the dot products per corner... just like the method says
+ int[] corners = {0,0,0,0},
+ B_counts = getDotProductsPerCorner(line, current, current_dots, previous, previous_dots, corners);
+
+ int B_current = B_counts[0],
+ B_previous = B_counts[1],
+ B_sum = B_counts[2];
+
+ // cases (1) or (6)? no collision.
+ if (B_sum == 0 || B_sum == 8) { return null; }
+
+ // case (4) or (5)? no collision, by convention.
+ if (B_previous < 4) { return null; }
+
+ // what remains is case (2) or (3)
if (B_previous == 4) {
- int currentCase = 2;
- // for (2) and (3) the approach is essentially
- // the same, except that we need different bounding
- // boxes to determine the intersection. For (2) we
- // can use the [current] bounding box, but for (3)
- // we need to construct a new box that is spanned by
- // the two points next to the "hot" corner in both
- // [previous] and [current].
- //
- // FIXME: that said, if we treat (2) as a subset
- // of (3), things seem to work better.
- checkLines = new float[8];
- arrayCopy(current,0,checkLines,0,8);
-
- currentCase = 3;
-
- // two of these edges are guaranteed to not have intersections,
- // since otherwise the intersection would be inside either
- // [previous] or [current], which is case (2) instead.
- checkLines = new float[]{previous[(corner)%8], previous[(corner+1)%8],
+
+ // first, we need to find out which corner point actually
+ // intersects our boundary. Typically this will be the
+ // first ranked corner, but not always.
+ float x1, y1, x2, y2;
+ int corner = -1;
+ float[] intersection;
+ for(int i=0, last=corners.length; i<last; i++) {
+ corner = corners[i];
+ x1 = previous[corner];
+ y1 = previous[corner+1];
+ x2 = current[corner];
+ y2 = current[corner+1];
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ if(intersection!=null) break;
+ }
+
+ // get the guaranteed hit-line
+ float px = previous[corner],
+ py = previous[corner+1],
+ cx = current[corner],
+ cy = current[corner+1];
+
+ // We now have the corner that's going to pass through
+ // the boundary first. we construct a new bbox to
+ // perform poly-through-line detection.
+ checkLines = new float[]{px, py,
previous[(corner+2)%8], previous[(corner+3)%8],
current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
+ cx, cy};
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
intersections.clear();
- float x1=checkLines[0], y1=checkLines[1], x2=x1, y2=y1;
+ x1=checkLines[0];
+ y1=checkLines[1];
+ x2=x1;
+ y2=y1;
for (int i=0, last=checkLines.length; i<last; i+=2) {
x2 = checkLines[(i+2)%last];
y2 = checkLines[(i+3)%last];
- float[] intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
if (intersection!=null) {
intersections.add(intersection);
if(intersections.size()==2) { break; }}
@@ -997,12 +999,6 @@ static class CollisionDetection {
// (We can have zero, one, or two)
int intersectionCount = intersections.size();
- // get the supposed hit-line
- float px = previous[corner],
- py = previous[corner+1],
- cx = current[corner],
- cy = current[corner+1];
-
// if we have two intersections, it's
// relatively easy to determine by how
// much we should move back.
@@ -1012,21 +1008,7 @@ static class CollisionDetection {
i2 = intersections.get(1);
float[] ideal = getLineLineIntersection(px,py,cx,cy, i1[0],i1[1],i2[0],i2[1], false);
if (ideal == null) {
- if(debug) println("error: could not find the case "+currentCase+" ideal point based on corner ["+corner+"]");
- /*
- println("tried to find the intersection between:");
- println("[1] "+px+","+py+","+cx+","+cy+" (blue)");
- println("[2] "+i1[0]+","+i1[1]+","+i2[0]+","+i2[1]+" (green)");
- //debugfunctions_drawBoundingBox(current,sketch);
- //debugfunctions_drawBoundingBox(previous,sketch);
- debugfunctions_drawBoundingBox(checkLines,sketch);
-
- sketch.noLoop();
- sketch.stroke(0,200,255);
- sketch.line(px,py,cx,cy);
- sketch.stroke(0,255,0);
- sketch.line(i1[0],i1[1],i2[0],i2[1]);
- */
+ if(debug) println("error: could not find the ideal point based on corner ["+corner+"]");
return new float[]{px-cx, py-cy, corner};
}
return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
@@ -1063,7 +1045,8 @@ static class CollisionDetection {
}
// Uncaught cases get a pass - with a warning.
-// println("unknown case! (B_current: "+B_current+", B_previous: "+B_previous+")");
+ // At least theoretically, there are no uncaught cases.
+ // But as a programmer, I can be wrong.
return null;
}
@@ -1150,8 +1133,7 @@ static class CollisionDetection {
// And then return the transformed coordinates, plus angle used
return new float[] {x1n, y1n, x2n, y2n, 0, 0, x4n, 0, angle};
}
-}
-/**
+}/**
* A static computation class.
*
* At the moment this houses the actor/boundary interaction
@@ -1977,7 +1959,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -1989,7 +1976,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -2275,7 +2267,9 @@ class Position {
// overlap
float angle = atan2(dy,dx);
if(angle<0) { angle += 2*PI; }
- return new float[]{dx, dy, angle};
+ float safedx = dw-dx,
+ safedy = dh-dy;
+ return new float[]{dx, dy, angle, safedx, safedy};
}
/**
@@ -2375,6 +2369,10 @@ abstract class Positionable extends Position implements Drawable {
void detachFromAll() {
boundaries.clear();
}
+
+ void rewind() {
+ copyFrom(previous);
+ }
/**
* change the position, relative
@@ -2593,7 +2591,6 @@ abstract class Positionable extends Position implements Drawable {
// we're attached to one or more boundaries, so we
// are subject to (compound) impulse redirection.
if(boundaries.size()>0) {
-// println("redirecting impulse {"+_dx+","+_dy+"} over boundary surfaces...");
float[] redirected = new float[]{_dx, _dy};
for(int b=boundaries.size()-1; b>=0; b--) {
Boundary boundary = boundaries.get(b);
@@ -2602,7 +2599,6 @@ abstract class Positionable extends Position implements Drawable {
continue;
}
redirected = boundary.redirectForce(redirected[0], redirected[1]);
-// println("redirected to {"+redirected[0]+","+redirected[1]+"}.");
}
x += redirected[0];
y += redirected[1];
@@ -4024,7 +4020,7 @@ void drawBackground(float width, float height) {
/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
-///*
+/*
final int screenWidth = 512;
final int screenHeight = 432;
@@ -4035,71 +4031,43 @@ void initialize() {
class TestLevel extends Level {
TestLevel(float w, float h) {
super(w,h);
- addLevelLayer("test", new TestLayer(this,w,h));
+ addLevelLayer("test", new TestLayer(this));
}
void draw() {
- fill(0,1);
+ fill(0,10);
rect(-1,-1,width+2,height+2);
super.draw();
- stroke(0,255,0,150);
- line(width/2,0,width/2,height);
- line(width/2+16,0,width/2+16,height);
}
}
class TestLayer extends LevelLayer {
TestObject t1, t2, t3, t4;
- TestLayer(Level p, float w, float h) {
- super(p,w,h);
+ TestLayer(Level p) {
+ super(p,p.width,p.height);
showBoundaries = true;
+
+ int v = 10;
- t1 = new TestObject(width/4, height/4);
+ t1 = new TestObject(width/2+v/2, height/2-200);
t1.setForces(5,0);
+
+ t1.setForces(0,0.01);
+ t1.setAcceleration(0,0.1);
- t2 = new TestObject(width/4, 3*height/4);
- t2.setForces(0,-5);
-
- t3 = new TestObject(3*width/4, 3*height/4);
- t3.setForces(-5,0);
-
- t4 = new TestObject(3*width/4, height/4);
- t4.setForces(0,5);
-
-// addPlayer(t1);
- addPlayer(t2);
-// addPlayer(t3);
-// addPlayer(t4);
+ addPlayer(t1);
addBoundary(new Boundary(width/2+230,height,width/2+200,0));
- addBoundary(new Boundary(width/2-180,0,width/2-150,height));
-
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
addBoundary(new Boundary(width,height/2-200,0,height/2-120));
addBoundary(new Boundary(0,height/2+200,width,height/2+120));
- addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ addBoundary(new Boundary(width/2,height/2,width/2 + v,height/2));
}
- void draw() {
+ void draw() {
super.draw();
-
- if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
- else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
-
- if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
- else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
-
- if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
- else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
-
- if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
- else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
- }
-
- void mouseClicked(int mx, int my, int mb) {
- if(mb==RIGHT) { noLoop(); return; }
- boundaries.clear();
- addBoundary(new Boundary(width/2, height/2, mx, my));
+ viewbox.track(parent,t1);
}
}
@@ -4111,4 +4079,4 @@ class TestObject extends Player {
}
}
-//*/
+*/
View
11 mario/hero.pde
@@ -118,6 +118,17 @@ class Mario extends Player {
}
/**
+ * When we touch down on a boundary,
+ * and we're in our jump animation,
+ * just go back to the idle state.
+ */
+ void gotBlocked(Boundary b, float[] intersection) {
+ if(active.name=="jumping") {
+ setCurrentState("idle");
+ }
+ }
+
+ /**
* What happens when we touch another actor?
*/
void overlapOccurredWith(Actor other, float[] direction) {
View
6 mario/mainlayer.pde
@@ -62,8 +62,8 @@ class MainLevelLayer extends LevelLayer {
// and let's add the thing that makes us win!
addGoal(1920, height-48);
-// showBoundaries = true;
-// showTriggers = true;
+ showBoundaries = true;
+ showTriggers = true;
}
/**
@@ -210,7 +210,7 @@ class MainLevelLayer extends LevelLayer {
super.draw();
viewbox.track(parent, mario);
// just in case!
- if(mario!=null && mario.active != null && mario.active.name!="dead" && mario.y>height) {
+ if(mario!=null && mario.active != null && mario.active.name!="dead" && mario.y>height*2) {
reset();
}
}
View
4 processing.js
@@ -19763,7 +19763,9 @@
}
// DEBUGGING HACK PATCH TEST TEST TEST
- return new Processing(canvas, code.join("\n"));
+ var sktch = new Processing(canvas, code.join("\n"));
+ sktch.noLoop();
+ return sktch;
// } catch(e) {
// throw "Processing.js: Unable to execute pjs sketch: " + e;
// }
View
62 spriteengine.pde
@@ -12,7 +12,7 @@
/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
-///*
+/*
final int screenWidth = 512;
final int screenHeight = 432;
@@ -23,71 +23,43 @@ void initialize() {
class TestLevel extends Level {
TestLevel(float w, float h) {
super(w,h);
- addLevelLayer("test", new TestLayer(this,w,h));
+ addLevelLayer("test", new TestLayer(this));
}
void draw() {
- fill(0,1);
+ fill(0,10);
rect(-1,-1,width+2,height+2);
super.draw();
- stroke(0,255,0,150);
- line(width/2,0,width/2,height);
- line(width/2+16,0,width/2+16,height);
}
}
class TestLayer extends LevelLayer {
TestObject t1, t2, t3, t4;
- TestLayer(Level p, float w, float h) {
- super(p,w,h);
+ TestLayer(Level p) {
+ super(p,p.width,p.height);
showBoundaries = true;
+
+ int v = 10;
- t1 = new TestObject(width/4, height/4);
+ t1 = new TestObject(width/2+v/2, height/2-200);
t1.setForces(5,0);
+
+ t1.setForces(0,0.01);
+ t1.setAcceleration(0,0.1);
- t2 = new TestObject(width/4, 3*height/4);
- t2.setForces(0,-5);
-
- t3 = new TestObject(3*width/4, 3*height/4);
- t3.setForces(-5,0);
-
- t4 = new TestObject(3*width/4, height/4);
- t4.setForces(0,5);
-
-// addPlayer(t1);
- addPlayer(t2);
-// addPlayer(t3);
-// addPlayer(t4);
+ addPlayer(t1);
addBoundary(new Boundary(width/2+230,height,width/2+200,0));
- addBoundary(new Boundary(width/2-180,0,width/2-150,height));
-
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
addBoundary(new Boundary(width,height/2-200,0,height/2-120));
addBoundary(new Boundary(0,height/2+200,width,height/2+120));
- addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ addBoundary(new Boundary(width/2,height/2,width/2 + v,height/2));
}
- void draw() {
+ void draw() {
super.draw();
-
- if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
- else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
-
- if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
- else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
-
- if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
- else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
-
- if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
- else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
- }
-
- void mouseClicked(int mx, int my, int mb) {
- if(mb==RIGHT) { noLoop(); return; }
- boundaries.clear();
- addBoundary(new Boundary(width/2, height/2, mx, my));
+ viewbox.track(parent,t1);
}
}
@@ -99,4 +71,4 @@ class TestObject extends Player {
}
}
-//*/
+*/
View
102 spriteengine.pde.old
@@ -0,0 +1,102 @@
+/***********************************
+ * *
+ * This file does nothing, *
+ * but allows Processing to *
+ * actually load the code *
+ * if located in a directory *
+ * of the same name. Feel *
+ * free to rename it. *
+ * *
+ ***********************************/
+
+
+/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
+
+/*
+
+final int screenWidth = 512;
+final int screenHeight = 432;
+void initialize() {
+ addLevel("test", new TestLevel(width,height));
+}
+
+class TestLevel extends Level {
+ TestLevel(float w, float h) {
+ super(w,h);
+ addLevelLayer("test", new TestLayer(this,w,h));
+ }
+
+ void draw() {
+ fill(0,1);
+ rect(-1,-1,width+2,height+2);
+ super.draw();
+ stroke(0,255,0,150);
+ line(width/2,0,width/2,height);
+ line(width/2+16,0,width/2+16,height);
+ }
+}
+
+class TestLayer extends LevelLayer {
+ TestObject t1, t2, t3, t4;
+ TestLayer(Level p, float w, float h) {
+ super(p,w,h);
+ showBoundaries = true;
+
+ t1 = new TestObject(width/4, height/4);
+ t1.setForces(5,0);
+
+ t2 = new TestObject(width/4, 3*height/4);
+ t2.setForces(0,-5);
+
+ t3 = new TestObject(3*width/4, 3*height/4);
+ t3.setForces(-5,0);
+
+ t4 = new TestObject(3*width/4, height/4);
+ t4.setForces(0,5);
+
+// addPlayer(t1);
+ addPlayer(t2);
+// addPlayer(t3);
+// addPlayer(t4);
+
+ addBoundary(new Boundary(width/2+230,height,width/2+200,0));
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
+
+ addBoundary(new Boundary(width,height/2-200,0,height/2-120));
+ addBoundary(new Boundary(0,height/2+200,width,height/2+120));
+
+ addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ }
+
+ void draw() {
+ super.draw();
+
+ if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
+ else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
+
+ if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
+ else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
+
+ if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
+ else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
+
+ if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
+ else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
+ }
+
+ void mouseClicked(int mx, int my, int mb) {
+ if(mb==RIGHT) { noLoop(); return; }
+ boundaries.clear();
+ addBoundary(new Boundary(width/2, height/2, mx, my));
+ }
+}
+
+class TestObject extends Player {
+ TestObject(float x, float y) {
+ super("test");
+ addState(new State("test","docs/tutorial/graphics/mario/small/Standing-mario.gif"));
+ setPosition(x,y);
+ }
+}
+
+*/
View
4,084 tests/backup codebase/codebase.pde
4,084 additions, 0 deletions not shown
View
348 tests/boundaries/interaction/CollisionDetection.pde
@@ -0,0 +1,348 @@
+/**
+ * Alternative collision detection
+ */
+static class CollisionDetection {
+ private static boolean debug = true;
+
+ /**
+ * Static classes need global sketch binding
+ */
+ private static PApplet sketch;
+ public static void init(PApplet s) { sketch = s; }
+
+ /**
+ * Perform line/rect intersection detection. Lines represent boundaries,
+ * and rather than doing "normal" line/rect intersection using a
+ * "box on a trajectory" that normal actor movement looks like, we pretend
+ * the actor box remains stationary, and move the boundary in the opposite
+ * direction with the same speed, which gives us a "boundary box", so that
+ * we can perform box/box overlap detection instead.
+ */
+ static float[] getLineRectIntersection(float[] line, float[] previous, float[] current)
+ {
+ if(debug) sketch.println(sketch.frameCount + "> testing against: "+arrayToString(line));
+ if(debug) sketch.println(sketch.frameCount + "> previous: "+arrayToString(previous));
+ if(debug) sketch.println(sketch.frameCount + "> current : "+arrayToString(current));
+
+ // First, let's do some dot-product math, to find out whether or not
+ // the actor's bounding box is even in range of the boundary.
+ float x1=line[0], y1=line[1], x2=line[2], y2=line[3];
+ float dx = x2-x1, dy = y2-y1, pv=PI/2.0,
+ rdx = dx*cos(pv) - dy*sin(pv),
+ rdy = dx*sin(pv) + dy*cos(pv);
+
+ // determine range w.r.t. the starting point of the boundary.
+ float[] dotProducts1 = getDotProducts(x1,y1,x2,y2, previous);
+
+ // determine range w.r.t. the end point of the boundary.
+ float[] dotProducts2 = getDotProducts(x2,y2,x1,y1, previous);
+
+ // determine 'sidedness', relative to the boundary.
+ float[] dotProducts3 = getDotProducts(x1,y1,x1+rdx,y1+rdy, previous);
+ float[] dotProducts4 = getDotProducts(x1,y1,x1+rdx,y1+rdy, current);
+
+ // compute the relevant feature values based on the dot products:
+ int inRangeS = 4, inRangeE = 4, above = 0, aboveAfter = 0;
+ for(int i=0; i<8; i+=2) {
+ if (dotProducts1[i] < 0) { inRangeS--; }
+ if (dotProducts2[i] < 0) { inRangeE--; }
+ if (dotProducts3[i] <= 0) { above++; }
+ if (dotProducts4[i] <= 0) { aboveAfter++; }}
+
+ if(debug) sketch.println(sketch.frameCount +"> dotproduct result: "+inRangeS+"/"+inRangeE+"/"+above+"/"+aboveAfter);
+
+ // make sure to short-circuit if the actor cannot
+ // interact with the boundary because it is out of range.
+ if (inRangeS == 0 || inRangeE == 0) {
+ if(debug) sketch.println(sketch.frameCount +"> this boundary is not involved in collisions for this frame.");
+ return null;
+ }
+
+ if (above>0 && aboveAfter>above) {
+ if(debug) sketch.println(sketch.frameCount +"> this box is not involved in collisions for this frame (1).");
+ return null;
+ }
+
+ if (above==4 && aboveAfter==4) {
+ if(debug) sketch.println(sketch.frameCount +"> this box is not involved in collisions for this frame (2).");
+ return null;
+ }
+
+ // Now then, let's determine whether overlap will occur.
+ boolean found = false;
+
+ // We're in bounds: if 'above' is 4, meaning that our previous
+ // actor frame is on the blocking side of a boundary, and
+ // 'aboveAfter' is 0, meaning its current frame is on the other
+ // side of the boundary, then a collision MUST have occurred.
+ if (above==4 && aboveAfter==0) {
+ // note that in this situation, the overlap may look
+ // like full containment, where the actor's bounding
+ // box is fully contained by the boundary's box.
+ found = true;
+ if(debug) sketch.println(sketch.frameCount +"> collision detected (due to full containment).");
+ }
+
+ else {
+ // We're in bounds: do box/box intersection checking
+ // using the 'previous' box and the boundary-box.
+ dx = previous[0] - current[0];
+ dy = previous[1] - current[1];
+
+ // form boundary box
+ float[] bbox = {line[0], line[1],
+ line[2], line[3],
+ line[2]+dx, line[3]+dy,
+ line[0]+dx, line[1]+dy};
+
+ // do any of the "previous" edges intersect
+ // with any of the "boundary box" edges?
+ int i,j;
+ float[] p = previous, b = bbox, intersection;
+ for(i=0; i<8; i+=2) {
+ for(j=0; j<8; j+=2) {
+ intersection = getLineLineIntersection(p[i], p[i+1], p[(i+2)%8], p[(i+3)%8], b[j], b[j+1], b[(j+2)%8], b[(j+3)%8], false, true);
+ if (intersection != null) {
+ found = true;
+ if(debug) sketch.println(sketch.frameCount +"> collision detected on a box edge (box overlap).");
+ }
+ }
+ }
+ }
+
+ // Have we signaled any overlap?
+ if (found) {
+ float[] distances = getCornerDistances(x1,y1,x2,y2, previous, current);
+ int[] corners = rankCorners(distances);
+
+ if(debug) {
+ sketch.print(sketch.frameCount + "> ");
+ for(int i=0; i<4; i++) {
+ sketch.print(corners[i]+"="+distances[corners[i]]);
+ if(i<3) sketch.print(", "); }
+ sketch.println();
+ }
+
+ // Get the corner on the previous and current actor bounding
+ // box that will "hit" the boundary first.
+ int corner = 0;
+ float xp = previous[corners[corner]],
+ yp = previous[corners[corner]+1],
+ xc = current[corners[corner]],
+ yc = current[corners[corner]+1];
+
+ // The trajectory for this point may intersect with
+ // the boundary. If it does, we'll have all the information
+ // we need to move the actor back along its trajectory by
+ // an amount that will place it "on" the boundary, at the right spot.
+ float[] intersection = getLineLineIntersection(xp,yp,xc,yc, x1,y1,x2,y2, false, true);
+
+ if (intersection==null) {
+ println("nearest-to-boundary is actually not on the boundary itself. More complex math is required!");
+
+ // it's also possible that the first corner to hit the boundary
+ // actually never touches the boundary because it intersects only
+ // if the boundary is infinitely long. So... let's make that happen:
+ intersection = getLineLineIntersection(xp,yp,xc,yc, x1,y1,x2,y2, false, false);
+
+ if (intersection==null) {
+ println("line extension alone is not enoough...");
+
+ // FIXME: this is not satisfactory! A real solution should be implemented!
+ return new float[]{xp-xc, yp-yc}; // effect a full rewind for now
+ }
+
+ return new float[]{intersection[0] - xc, intersection[1] - yc};
+ }
+
+ // if we get here, there was a normal trajectory
+ // intersection with the boundary. Computing the
+ // corrective values by which to move the current
+ // frame's bounding box is really simple:
+ dx = intersection[0] - xc;
+ dy = intersection[1] - yc;
+
+ if(debug) sketch.println(sketch.frameCount +"> dx: "+dx+", dy: "+dy);
+
+ return new float[]{dx, dy};
+ }
+
+ return null;
+ }
+
+ /**
+ * For each corner in an object's bounding box, get the distance from its "previous"
+ * box to the line defined by (x1,y1,x2,y2). Return this as float[8], corresponding
+ * to the bounding box array format.
+ */
+ static float[] getCornerDistances(float x1, float y1, float x2, float y2, float[] previous, float[] current) {
+ float[] distances = {0,0,0,0,0,0,0,0}, intersection;
+ float dx, dy;
+ for(int i=0; i<8; i+=2) {
+ intersection = getLineLineIntersection(x1,y1,x2,y2, previous[i], previous[i+1], current[i], current[i+1], false, false);
+ if (intersection == null) {
+ continue;
+ }
+ dx = intersection[0] - previous[i];
+ dy = intersection[1] - previous[i+1];
+ distances[i] = sqrt(dx*dx+dy*dy);
+ distances[i+1] = distances[i];
+ }
+ return distances;
+ }
+
+
+ /**
+ * Get the intersection coordinate between two lines segments,
+ * using fairly standard, if a bit lenghty, linear algebra.
+ */
+ static float[] getLineLineIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, boolean colinearity, boolean segments)
+ {
+ float epsilon = 0.1;
+ // convert lines to the generatised form [a * x + b + y = c]
+ float a1 = -(y2 - y1), b1 = (x2 - x1), c1 = (x2 - x1) * y1 - (y2 - y1) * x1;
+ float a2 = -(y4 - y3), b2 = (x4 - x3), c2 = (x4 - x3) * y3 - (y4 - y3) * x3;
+ // find their intersection
+ float d = a1 * b2 - a2 * b1;
+ if (d == 0) {
+ // Two lines are parallel: we are not interested in the
+ // segment if the points are not colinear.
+ if (!colinearity || (x2 - x3) * (y2 - y1) != (y2 - y3) * (x2 - x1)) {
+ return null;
+ }
+ // Solve the algebraic functions [x = (x1 - x0) * t + x0] for t
+ float t1 = x3 != x4 ? (x1 - x3) / (x4 - x3) : (y1 - y3) / (y4 - y3);
+ float t2 = x3 != x4 ? (x2 - x3) / (x4 - x3) : (y2 - y3) / (y4 - y3);
+ if ((t1 < 0 && t2 < 0) || (t1 > 1 && t2 > 1)) {
+ // points 1 and 2 are outside the points 3 and 4 segment
+ return null;
+ }
+ // Clamp t values to the interval [0, 1]
+ t1 = t1 < 0 ? 0 : t1 > 1 ? 1 : t1;
+ t2 = t2 < 0 ? 0 : t2 > 1 ? 1 : t2;
+ return new float[]{(x4 - x3) * t1 + x3, (y4 - y3) * t1 + y3,
+ (x4 - x3) * t2 + x3, (y4 - y3) * t2 + y3};
+ }
+ // not colinear - find the intersection point
+ else {
+ float x = (c1 * b2 - c2 * b1) / d;
+ float y = (a1 * c2 - a2 * c1) / d;
+ // make sure the point can be found on both segments.
+ if (segments && (x < min(x1, x2) - epsilon || max(x1, x2) + epsilon < x ||
+ y < min(y1, y2) - epsilon || max(y1, y2) + epsilon < y ||
+ x < min(x3, x4) - epsilon || max(x3, x4) + epsilon < x ||
+ y < min(y3, y4) - epsilon || max(y3, y4) + epsilon < y)) {
+ // not on either, or both, segments.
+ return null;
+ }
+ return new float[]{x, y};
+ }
+ }
+
+
+ /**
+ * compute the dot product between all corner points of a
+ * bounding box, and a boundary line with origin ox/oy
+ * and end point tx/ty.
+ */
+ static float[] getDotProducts(float ox, float oy, float tx, float ty, float[] bbox) {
+ float dotx = tx-ox, doty = ty-oy,
+ otlen = sqrt(dotx*dotx + doty*doty),
+ dx, dy, len, dotproduct;
+
+ dotx /= otlen;
+ doty /= otlen;
+
+ float[] dotProducts = new float[8];
+
+ for(int i=0; i<8; i+=2) {
+ dx = bbox[i]-ox;
+ dy = bbox[i+1]-oy;
+ len = sqrt(dx*dx+dy*dy);
+ dx /= len;
+ dy /= len;
+ dotproduct = dx*dotx + dy*doty;
+ dotProducts[i] = dotproduct;
+ }
+
+ return dotProducts;
+ }
+
+
+ /**
+ * Rank the corner points for a bounding box
+ * based on it's distance to the boundary,
+ * along its trajectory path.
+ *
+ * We rank by decreasing distance.
+ */
+ // FIXME: this is a pretty fast code, but there might be
+ // better ways to achieve the desired result.
+ static int[] rankCorners(float[] distances) {
+ int[] corners = {0,0,0,0};
+ float corner1v=999999, corner2v=corner1v, corner3v=corner1v, corner4v=corner1v, distance;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
+
+ for(int i=0; i<8; i+=2) {
+ distance = distances[i];
+ if (distance < corner1v) {
+ corner4v = corner3v; corner4 = corner3;
+ corner3v = corner2v; corner3 = corner2;
+ corner2v = corner1v; corner2 = corner1;
+ corner1v = distance; corner1 = i;
+ continue; }
+ if (distance < corner2v) {
+ corner4v = corner3v; corner4 = corner3;
+ corner3v = corner2v; corner3 = corner2;
+ corner2v = distance; corner2 = i;
+ continue; }
+ if (distance < corner3v) {
+ corner4v = corner3v; corner4 = corner3;
+ corner3v = distance; corner3 = i;
+ continue; }
+ corner4v = distance; corner4 = i;
+ }
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
+ return corners;
+ }
+
+
+ /**
+ * Check if a bounding box's dot product
+ * information implies it's safe, or blocked.
+ */
+ static boolean permitted(float[] dotProducts) {
+ for(int i=0; i<8; i+=2) {
+ if (dotProducts[i]>0)
+ return true; }
+ return false;
+ }
+
+
+ /**
+ * Simple drawing helper function
+ */
+ static void drawBox(float[] boundingbox) {
+ sketch.line(boundingbox[0], boundingbox[1], boundingbox[2], boundingbox[3]);
+ sketch.line(boundingbox[2], boundingbox[3], boundingbox[4], boundingbox[5]);
+ sketch.line(boundingbox[4], boundingbox[5], boundingbox[6], boundingbox[7]);
+ sketch.line(boundingbox[6], boundingbox[7], boundingbox[0], boundingbox[1]);
+ }