Permalink
Browse files

2-intersect boundary collision fixed. 1-intersect still left. started…

… reimplementing mario for tutorial purposes
  • Loading branch information...
1 parent 0b3e577 commit 35974323429142f550ea874486911d056d4014f9 @Pomax committed Jun 11, 2012
Showing with 9,571 additions and 245 deletions.
  1. +10 −1 Actor.pde
  2. +33 −8 Boundary.pde
  3. +121 −35 CollisionDetection.pde
  4. +15 −0 GameLoader.pde
  5. +9 −0 LevelLayer.pde
  6. +2 −2 SoundManager.pde
  7. +5 −3 Sprite.pde
  8. +5 −4 SpritePathChunker.pde
  9. +32 −4 State.pde
  10. +11 −3 TilingSprite.pde
  11. +4 −0 build.bat
  12. +48 −0 docs/docs.pde
  13. +469 −148 docs/tutorial/codebase.pde
  14. BIN docs/tutorial/graphics/mario/small/Crouching-mario.gif
  15. BIN docs/tutorial/graphics/mario/small/Dead-mario.gif
  16. BIN docs/tutorial/graphics/mario/small/Jumping-mario.gif
  17. BIN docs/tutorial/graphics/mario/small/Looking-mario.gif
  18. BIN docs/tutorial/graphics/mario/small/Running-mario.gif
  19. BIN docs/tutorial/graphics/mario/small/Spinning-mario.gif
  20. BIN docs/tutorial/graphics/mario/small/Standing-mario.gif
  21. BIN docs/tutorial/sky.gif
  22. BIN mario/audio/Banzai.mp3
  23. BIN mario/audio/Banzai.ogg
  24. BIN mario/audio/Bonk.mp3
  25. BIN mario/audio/Bonk.ogg
  26. BIN mario/audio/Coin.mp3
  27. BIN mario/audio/Coin.ogg
  28. BIN mario/audio/Dead mario.mp3
  29. BIN mario/audio/Dead mario.ogg
  30. BIN mario/audio/Dragon coin.mp3
  31. BIN mario/audio/Dragon coin.ogg
  32. BIN mario/audio/Fireball.mp3
  33. BIN mario/audio/Fireball.ogg
  34. BIN mario/audio/Jump.mp3
  35. BIN mario/audio/Jump.ogg
  36. BIN mario/audio/Pipe.mp3
  37. BIN mario/audio/Pipe.ogg
  38. BIN mario/audio/Powerup.mp3
  39. BIN mario/audio/Powerup.ogg
  40. BIN mario/audio/Spin jump.mp3
  41. BIN mario/audio/Spin jump.ogg
  42. BIN mario/audio/Squish.mp3
  43. BIN mario/audio/Squish.ogg
  44. BIN mario/audio/bg/Bonus.mp3
  45. BIN mario/audio/bg/Bonus.ogg
  46. BIN mario/audio/bg/Course-clear.mp3
  47. BIN mario/audio/bg/Course-clear.ogg
  48. BIN mario/audio/bg/Overworld.mp3
  49. BIN mario/audio/bg/Overworld.ogg
  50. +15 −0 mario/background.pde
  51. +47 −0 mario/banzaibill.pde
  52. +4,067 −0 mario/codebase.pde
  53. BIN mario/fonts/acmesa.ttf
  54. BIN mario/graphics/assorted/Block.gif
  55. BIN mario/graphics/assorted/Coin-block-exhausted.gif
  56. BIN mario/graphics/assorted/Coin-block.gif
  57. BIN mario/graphics/assorted/Dragon-coin.gif
  58. BIN mario/graphics/assorted/Flower.gif
  59. BIN mario/graphics/assorted/Flowerpower.gif
  60. BIN mario/graphics/assorted/Goal-back.gif
  61. BIN mario/graphics/assorted/Goal-front.gif
  62. BIN mario/graphics/assorted/Goal-slider.gif
  63. BIN mario/graphics/assorted/Mushroom.gif
  64. BIN mario/graphics/assorted/Passthrough-block.gif
  65. BIN mario/graphics/assorted/Pipe-body.gif
  66. BIN mario/graphics/assorted/Pipe-head.gif
  67. BIN mario/graphics/assorted/Regular-coin.gif
  68. BIN mario/graphics/backgrounds/bonus.gif
  69. BIN mario/graphics/backgrounds/bush-01.gif
  70. BIN mario/graphics/backgrounds/bush-02.gif
  71. BIN mario/graphics/backgrounds/bush-03.gif
  72. BIN mario/graphics/backgrounds/bush-04.gif
  73. BIN mario/graphics/backgrounds/bush-05.gif
  74. BIN mario/graphics/backgrounds/cave-corner-left.gif
  75. BIN mario/graphics/backgrounds/cave-corner-right.gif
  76. BIN mario/graphics/backgrounds/cave-filler.gif
  77. BIN mario/graphics/backgrounds/cave-side-left.gif
  78. BIN mario/graphics/backgrounds/cave-side-right.gif
  79. BIN mario/graphics/backgrounds/cave-top.gif
  80. BIN mario/graphics/backgrounds/ground-corner-left.gif
  81. BIN mario/graphics/backgrounds/ground-corner-right.gif
  82. BIN mario/graphics/backgrounds/ground-filler.gif
  83. BIN mario/graphics/backgrounds/ground-side-left.gif
  84. BIN mario/graphics/backgrounds/ground-side-right.gif
  85. BIN mario/graphics/backgrounds/ground-slant.gif
  86. BIN mario/graphics/backgrounds/ground-top.gif
  87. BIN mario/graphics/backgrounds/sky.gif
  88. BIN mario/graphics/backgrounds/sky_2.gif
  89. BIN mario/graphics/decals/100.gif
  90. BIN mario/graphics/decals/1000.gif
  91. BIN mario/graphics/decals/200.gif
  92. BIN mario/graphics/decals/300.gif
  93. BIN mario/graphics/decals/400.gif
  94. BIN mario/graphics/decals/500.gif
  95. BIN mario/graphics/enemies/Banzai-bill.gif
  96. BIN mario/graphics/enemies/Boo-chasing.gif
  97. BIN mario/graphics/enemies/Boo-waiting.gif
  98. BIN mario/graphics/enemies/Coin-boo-transition.gif
  99. BIN mario/graphics/enemies/Dead-koopa.gif
  100. BIN mario/graphics/enemies/Naked-koopa-walking.gif
  101. BIN mario/graphics/enemies/Red-koopa-flying.gif
  102. BIN mario/graphics/enemies/Red-koopa-standing.gif
  103. BIN mario/graphics/enemies/Red-koopa-walking.gif
  104. BIN mario/graphics/mario/big/Crouching-mario.gif
  105. BIN mario/graphics/mario/big/Jumping-mario.gif
  106. BIN mario/graphics/mario/big/Looking-mario.gif
  107. BIN mario/graphics/mario/big/Running-mario.gif
  108. BIN mario/graphics/mario/big/Spinning-mario.gif
  109. BIN mario/graphics/mario/big/Standing-mario.gif
  110. BIN mario/graphics/mario/fire/Crouching-mario.gif
  111. BIN mario/graphics/mario/fire/Jumping-mario.gif
  112. BIN mario/graphics/mario/fire/Looking-mario.gif
  113. BIN mario/graphics/mario/fire/Running-mario.gif
  114. BIN mario/graphics/mario/fire/Spinning-mario.gif
  115. BIN mario/graphics/mario/fire/Standing-mario.gif
  116. BIN mario/graphics/mario/small/Crouching-mario.gif
  117. BIN mario/graphics/mario/small/Dead-mario.gif
  118. BIN mario/graphics/mario/small/Jumping-mario.gif
  119. BIN mario/graphics/mario/small/Looking-mario.gif
  120. BIN mario/graphics/mario/small/Running-mario.gif
  121. BIN mario/graphics/mario/small/Spinning-mario.gif
  122. BIN mario/graphics/mario/small/Standing-mario.gif
  123. BIN mario/graphics/mario/small/Winner-mario.gif
  124. BIN mario/graphics/mute.gif
  125. BIN mario/graphics/unmute.gif
  126. +174 −0 mario/hero.pde
  127. +36 −0 mario/index.html
  128. +56 −0 mario/koopa.pde
  129. +207 −0 mario/mainlayer.pde
  130. +15 −0 mario/mainlevel.pde
  131. +34 −0 mario/mario.pde
  132. BIN mario/mute.gif
  133. +74 −0 mario/pickups.pde
  134. +84 −0 mario/preloads.pde
  135. +50 −0 mario/triggers.pde
  136. BIN mario/unmute.gif
  137. +11 −0 spriteengine.pde
  138. +3,785 −0 tests/codebase.pde
  139. +11 −33 tests/index.html
  140. +4 −4 tests/test.pde
  141. +137 −0 tests/test2.pde
View
@@ -77,6 +77,14 @@ abstract class Actor extends Positionable {
}
/**
+ * Get the current sprite image
+ */
+ PImage getSpriteMask() {
+ if(active == null) return null;
+ return active.sprite.getFrame();
+ }
+
+ /**
* Tell this actor which layer it is operating in
*/
void setLevelLayer(LevelLayer layer) {
@@ -225,6 +233,7 @@ abstract class Actor extends Positionable {
y = round(y+dy);
ix = 0;
iy = 0;
+ aFrameCount = 0;
}
/**
@@ -281,7 +290,7 @@ abstract class Actor extends Positionable {
* Draw preprocessing happens here.
*/
void draw(float vx, float vy, float vw, float vh) {
- handleInput();
+ if(!remove) handleInput();
super.draw(vx,vy,vw,vh);
}
View
@@ -7,7 +7,7 @@ class Boundary extends Positionable {
private float PI2 = 2*PI;
// extended adminstrative values
- float dx, dy;
+ float dx, dy, length;
float xw, yh;
float minx, maxx, miny, maxy;
float angle, cosa, sina, cosma, sinma;
@@ -35,6 +35,7 @@ class Boundary extends Positionable {
// deltas
dx = x2-x1;
dy = y2-y1;
+ length = sqrt(dx*dx+dy*dy);
updateBounds();
updateAngle();
glide = 1.0;
@@ -101,13 +102,37 @@ class Boundary extends Positionable {
* supported by this boundary?
*/
boolean supports(Positionable thing) {
- float epsilon = 1;
- float[] bbox = thing.getBoundingBox();
- if (minx - epsilon > bbox[2]) return false;
- if (miny - epsilon > bbox[5]) return false;
- if (maxx + epsilon < bbox[0]) return false;
- if (maxy + epsilon < bbox[1]) return false;
- return true;
+ float[] bbox = thing.getBoundingBox(), nbox = new float[8];
+
+ // First, translate all coordinates so that they're
+ // relative to the boundary's (x,y) coordinate.
+ bbox[0] -= x; bbox[1] -= y;
+ bbox[2] -= x; bbox[3] -= y;
+ bbox[4] -= x; bbox[5] -= y;
+ bbox[6] -= x; bbox[7] -= y;
+
+ // Then, rotate the bounding box so that it's
+ // axis-aligned with the boundary line.
+ nbox[0] = bbox[0] * cosma - bbox[1] * sinma;
+ nbox[1] = bbox[0] * sinma + bbox[1] * cosma;
+ nbox[2] = bbox[2] * cosma - bbox[3] * sinma;
+ nbox[3] = bbox[2] * sinma + bbox[3] * cosma;
+ nbox[4] = bbox[4] * cosma - bbox[5] * sinma;
+ nbox[5] = bbox[4] * sinma + bbox[5] * cosma;
+ nbox[6] = bbox[6] * cosma - bbox[7] * sinma;
+ nbox[7] = bbox[6] * sinma + bbox[7] * cosma;
+
+ // Get new bounding box minima/maxima
+ float mx = min(min(nbox[0],nbox[2]),min(nbox[4],nbox[6])),
+ MX = max(max(nbox[0],nbox[2]),max(nbox[4],nbox[6])),
+ my = min(min(nbox[1],nbox[3]),min(nbox[5],nbox[7])),
+ MY = max(max(nbox[1],nbox[3]),max(nbox[5],nbox[7]));
+
+ // Now, determine whether we're "off" the boundary...
+ boolean outOfBounds = (mx > length) || (MX < 0) || (MY<-1.99);
+
+ // if the thing's not out of bounds, it's supported.
+ return !outOfBounds;
}
/**
View
@@ -11,54 +11,128 @@ static class CollisionDetection {
private static PApplet sketch;
public static void init(PApplet s) { sketch = s; }
+
/**
* Perform actor/boundary collision detection
*/
static void interact(Boundary b, Actor a)
{
if (a.isAttachedTo(b)) { return; }
- float[] correction = blocks(b,a);
+ float[] bbox = a.getBoundingBox(),
+ correction = blocks(b,a,bbox);
+
if(correction != null) {
- /*
- sketch.stroke(0);
- sketch.line(a.getX()-10, a.getY()-10, a.getX()+10, a.getY()+10);
- sketch.line(a.getX()-10, a.getY()+10, a.getX()+10, a.getY()-10);
-
- sketch.stroke(255,0,0);
- sketch.line(a.getX(), a.getY(), a.getX()+correction[0], a.getY()+correction[1]);
- */
- a.attachTo(b, correction); }
+ //adjustForSpriteMask(b, a, bbox, correction);
+ a.attachTo(b, correction);
+ }
}
+
/**
* Is this boundary blocking the specified actor?
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
*/
- static float[] blocks(Boundary b, Actor a)
+ static float[] blocks(Boundary b, Actor a, float[] bbox)
{
// we don't block in the pass-through direction.
if(b.allowPassThrough(a.ix, a.iy)) { return null; }
- float[] current = a.getBoundingBox(),
+ float[] current = bbox,
previous = a.previous.getBoundingBox(),
line = {b.x, b.y, b.xw, b.yh};
return CollisionDetection.getLineRectIntersection(line, current, previous);
}
+
+ /**
+ * The correction moves the bounding box to a safe location on the boundary,
+ * but this might not be the correct location given the sprite inside the
+ * bounding box. In order to make things look 'right', we examine the bounding
+ * box to see by how much we can decrease the correction so that it looks as
+ * if the sprite is anchored to the boundary.
+ *
+ * Does not return anything. The correction array is modified in-place.
+ */
+ static void adjustForSpriteMask(Boundary b, Actor a, float[] bbox, float[] correction)
+ {
+ int corner = (int)correction[2];
+ if (corner == -1) return;
+
+ int sx = (int) bbox[corner],
+ sy = (int) bbox[corner+1],
+ ex = (int) (bbox[corner] - a.ix),
+ ey = (int) (bbox[corner+1] - a.iy),
+ minx = (int) bbox[0], // FIXME: this will be incorrect for rotated actors
+ miny = (int) bbox[1];
+ PImage mask = a.getSpriteMask();
+ if (mask != null) {
+ int[] distance = getPermissibleSpriteMaskShift(mask, sx,sy, ex,ey, minx,miny);
+ correction[0] -= distance[0];
+ correction[1] -= distance[1];
+ }
+ }
+
+
+ /**
+ * Find the safe distance by which we can move a sprite so that
+ * rather than having its corner touch a boundary, it will look
+ * like the sprite's image touches the boundary instead.
+ *
+ * This uses Bresenham's line algorithm, with a
+ * safety conditional to prevent infinite loops.
+ */
+ static int[] getPermissibleSpriteMaskShift(PImage m, int x0, int y0, int x1, int y1, int minx, int miny)
+ {
+ int[] shift = {0,0};
+ int sx = x0,
+ sy = y0,
+ dx = (int) abs(x1-x0), // distance to travel
+ dy = (int) abs(y1-y0),
+ ax = (x0<x1) ? 1 : -1, // x and y stepping values
+ ay = (y0<y1) ? 1 : -1,
+ imx = 0, // x and y coordinate on the image mask
+ imy = 0,
+ err = dx-dy,
+ // Conceptually you rely on "while(true)", but no one
+ // should ever do that. Safety first: add a cutoff.
+ // Verifying over a 65535 pixel line should be enough.
+ safety = 0;
+ while(safety++ < 0xFFFF) {
+ // if we find a pixel in the mask that is not
+ // transparent, we have found our safe distance.
+ imx = x0-minx;
+ imy = y0-miny;
+ if(sketch.alpha(m.get(imx,imy)) > 0) {
+ shift = new int[]{(x0-sx), (y0-sy)};
+ return shift;
+ }
+ // We must stop when we've reached the end coordinate
+ else if(x0==x1 && y0==y1) {
+ return shift;
+ }
+ // continue: move to the next pixel
+ int e2 = err<<1;
+ if(e2 > -dy) { err -= dy; x0 += ax; }
+ if(e2 < dx) { err += dx; y0 += ay; }
+ }
+ // This code is unreachable, but ONLY in theory.
+ // We could still get here if the cutoff is reached.
+ return shift;
+ }
+
+
// =================================================================
+ // recycled containers
private static float[] current_dots = {0,0,0,0,0,0,0,0};
private static float[] previous_dots = {0,0,0,0,0,0,0,0};
private static ArrayList<float[]> intersections = new ArrayList<float[]>();
private static float[] checkLines = {0,0,0,0,0,0,0,0};
- private static float[] ZERO_DIFFERENCE = {0,0};
-
- // constant for indicating a coordinate is an intersection
- final static int INTERSECTION = 0;
-
- // constant for indicating a coordinate is a full contained coordinate
- final static int CONTAINED = 1;
+ private static float[] ZERO_DIFFERENCE = {0,0,-1};
/**
@@ -96,6 +170,16 @@ static class CollisionDetection {
* 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
*/
static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
{
@@ -181,22 +265,23 @@ static class CollisionDetection {
checkLines = new float[8];
arrayCopy(current,0,checkLines,0,8);
- // if we're seeing case (3), set up bbox correctly.
- if (B_current < 4) {
+// // if we're seeing case (3), set up bbox correctly.
+// if (B_current < 4) {
// println("case 3");
- 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],
- previous[(corner+2)%8], previous[(corner+3)%8],
- current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
- }
- else
- {
+
+ 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],
+ previous[(corner+2)%8], previous[(corner+3)%8],
+ current[(corner+2)%8], current[(corner+3)%8],
+ current[(corner)%8], current[(corner+1)%8]};
+// }
+// else
+// {
// println("case 2");
- }
+// }
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
@@ -247,10 +332,11 @@ static class CollisionDetection {
sketch.stroke(0,255,0);
sketch.line(i1[0],i1[1],i2[0],i2[1]);
*/
- return new float[]{px-cx, py-cy};
+ return new float[]{px-cx, py-cy, corner};
}
- return new float[]{ideal[0]-cx, ideal[1]-cy};
+ return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
}
+
// if there's only one intersection,
// additional math is required.
else if (intersectionCount == 1) {
View
@@ -68,3 +68,18 @@ void removeLevel(String name) {
levelSet.remove(name);
}
}
+
+/**
+ * Get a specific level (for debug purposes)
+ */
+Level getLevel(String name) {
+ return levelSet.get(name);
+}
+
+/**
+ * clear all levels
+ */
+void clearLevels() {
+ levelSet = new HashMap<String, Level>();
+ activeLevel = null;
+}
View
@@ -197,6 +197,13 @@ abstract class LevelLayer {
yScale = sy;
nonstandard = (xScale!=1 || yScale!=1 || xTranslate!=0 || yTranslate!=0);
}
+
+ /**
+ * Get the level this layer exists in
+ */
+ Level getLevel() {
+ return parent;
+ }
// used for statistics
int getActorCount() {
@@ -386,10 +393,12 @@ abstract class LevelLayer {
if(showActors) {
for(int i=players.size()-1; i>=0; i--) {
Player a = players.get(i);
+
if(a.remove) {
players.remove(i);
if(javascript!=null) { javascript.removeActor(); }
continue; }
+
if(a.interacting) {
// boundary interference?
View
@@ -93,8 +93,8 @@ static class SoundManager {
ap.rewind();
}
- static void mute() {
- muted = !muted;
+ static void mute(boolean _muted) {
+ muted = _muted;
for(AudioPlayer ap: audioplayers.values()) {
if(muted) { ap.mute(); }
else { ap.unmute(); }
View
@@ -181,7 +181,7 @@ class Sprite extends Positionable {
PImage getFrame() {
// update sprite based on path frames
currentFrame = getCurrentFrameNumber();
- if (path != null && path.size()>0) {
+ if (path.size()>0) {
float[] pathdata = path.getNextFrameInformation();
setScale(pathdata[2], pathdata[3]);
setRotation(pathdata[4]);
@@ -219,10 +219,12 @@ class Sprite extends Positionable {
* at the correct location.
*/
void draw() { draw(0,0); }
- void draw(float x, float y) {
+ void draw(float px, float py) {
if (visible) {
PImage img = getFrame();
- image(img, x + ox - halfwidth, y + oy - halfheight);
+ float imx = x + px + ox - halfwidth,
+ imy = y + py + oy - halfheight;
+ image(img, imx, imy);
}
}
Oops, something went wrong.

0 comments on commit 3597432

Please sign in to comment.