// Bungee Fisher by Patricio Davila // using dangly pendulum by Jeffrey Traer Bernstein import traer.physics.*; ParticleSystem physics; Particle[] particles; import ddf.minim.*; AudioSample splash; AudioSample grunt; AudioSample pop; PImage backgroundImage, diver, fish, heart, bottle; FloatingObject[] fishes = new FloatingObject[7]; // set number of fishes at a time FloatingObject[] bottles = new FloatingObject[2]; // set number of bottles at a time DiverClass theDiver; Health healthScore; PFont font; // basic collision variables float x1, y1, x2, y2; float r1 = 10, r2 = 10; // radius of objects float rSum = r1+r2; float speed = 1.5, directionX = .4, directionY = .75, velocityX, velocityY; int gray = 255; float d; Point[] polygonLeft, polygonRight; boolean insideRockLeftFlag = false; boolean insideRockRightFlag = false; boolean insideWaterFlag = false; float currentX, currentY; int score = 0; // time limits int onLow = 300; int onHigh = 600; int offLow = 100; int offHigh = 200; // areas int waterLeft = 0; int waterRight = 300; int waterTop = 520; int waterBottom = 580; void setup() { // load images backgroundImage = loadImage("background1c.jpg"); diver = loadImage("diver1a.png"); fish = loadImage("fish1a.png"); heart = loadImage("heart1a.png"); bottle = loadImage("bottle1a.png"); // load sounds Minim.start(this); splash = Minim.loadSample("splash1.wav", 512); grunt = Minim.loadSample("grunt1.wav", 512); pop = Minim.loadSample("pop1.wav", 512); // makes fishes for (int i = 0; i < fishes.length; i++) { fishes[i] = new FloatingObject(0,0); fishes[i].on = false; } // make bottles for (int i = 0; i < bottles.length; i++) { bottles[i] = new FloatingObject(0,0); bottles[i].on = false; } // make diver theDiver = new DiverClass(width/2, 80, diver.width, diver.height); // make rock areas rockAreas(); // make health score healthScore = new Health(30); // e.g 30 points equals 3 hearts // load font font = loadFont("Futura-36.vlw"); textAlign(CENTER); // setup canvas size( 300, 600 ); smooth(); noFill(); frameRate( 24 ); ellipseMode( CENTER ); // bungee physics physics = new ParticleSystem( 5.0, 0.05 ); particles = new Particle[10]; particles[0] = physics.makeParticle( 1.0, width/2, 90, 0); particles[0].makeFixed(); for ( int i = 1; i < particles.length; ++i ) { particles[i] = physics.makeParticle( 1.0, width/2, height/2+i, 0 ); // makeSpring( Particle a, Particle b, float strength, float damping, float restLength ) physics.makeSpring( particles[i-1], particles[i], 2.0, 0.5, 18 ); } particles[particles.length-1].setMass( 35.0 ); } void draw() { // bungee physics //physics.advanceTime( 1.0 ); // old method? physics.tick(1.0); // pick up bungee diver with mouse and restrain where mouse can pick up diver if ( mousePressed && mouseY < 100 ) { particles[particles.length-1].moveTo( mouseX, mouseY, 0 ); particles[particles.length-1].velocity().clear(); } // redraw background image(backgroundImage, 0, 0); // show score fill(255); textFont(font); text(score, 150, 50); // bungee physics noFill(); stroke(120); beginShape(); curveVertex( particles[0].position().x(), particles[0].position().y() ); for ( int i = 0; i < particles.length; ++i ) { curveVertex( particles[i].position().x(), particles[i].position().y() ); } curveVertex( particles[particles.length-1].position().x(), particles[particles.length-1].position().y() ); endShape(); // draw bungee rope origin fill(0); noStroke(); ellipse( particles[0].position().x(), particles[0].position().y(), 5, 5 ); // get current position of diver currentX = particles[particles.length-1].position().x(); currentY = particles[particles.length-1].position().y(); // draw diver theDiver.drawDiver(currentX - diver.width/2, currentY); // start fishes for (int i = 0; i < fishes.length; i++) { //println(i + " x:" + fishes[i].on + " y:" + fishes[i].x + " " + fishes[i].y + " startX:" + fishes[i].startPositionX + " endX:" + fishes[i].endPositionX + " sp:" + fishes[i].speed); if (fishes[i].on) { fishes[i].animate(fish); } else { fishes[i].start(); } } // start bottles for (int i = 0; i < bottles.length; i++) { //println(i + " x:" + fishes[i].on + " y:" + fishes[i].x + " " + fishes[i].y + " startX:" + fishes[i].startPositionX + " endX:" + fishes[i].endPositionX + " sp:" + fishes[i].speed); if (bottles[i].on) { bottles[i].animate(bottle); } else { bottles[i].start(); } } // splash water if (currentY > waterTop && !insideWaterFlag) { splash.trigger(); insideWaterFlag = true; } else if (currentY < 500) { insideWaterFlag = false; } // draw health score healthScore.displayHealth(); // object collision detection //checkFishCollision(); //checkBottleCollision(); checkCollision(fishes, 1); checkCollision(bottles, 2); checkRockCollision(currentX, currentY); //checkGameOver(healthScore.currentHealth, healthScore.maxHealth); } void mouseReleased() { // set origin of diver's jump particles[particles.length-1].setVelocity( (mouseX - pmouseX), (mouseY - pmouseY), 0 ); } class FloatingObject { float x, y; boolean hit; int onscreenTime, onscreenCount; int offscreenTime, offscreenCount; int startPositionX, startPositionY, endPositionX, endPositionY; boolean on; int speed; FloatingObject() { return; } FloatingObject(float xPos, float yPos) { x = xPos; y = yPos; } void start() { startPositionX = int(random(waterLeft, waterRight)); startPositionY = int(random(waterTop, waterBottom)); endPositionX = int(random(waterLeft,(startPositionX+20))); //endPositionY = startPositionY - int(random(0,(waterTop-startPositionY))); x = startPositionX; y = startPositionY; on = true; speed = int(random(1, 3)); } void animate(PImage img) { if (x <= startPositionX && x >= endPositionX) { x = x - speed; //y = y + 1; tint(255, 120); // make slightly transparent image(img, x, y); noTint(); } else { start(); } } } static class Point { float x, y; Point ( float x, float y ) { this.x = x; this.y = y; } } class DiverClass { float x, y, w, h, rad; DiverClass ( float xPos, float yPos, float wSize, float hSize ) { x = xPos; y = yPos; rad = 5; w = wSize; h = hSize; } void drawDiver ( float xPos, float yPos ) { x = xPos; y = yPos; image ( diver, x, y, diver.width, diver.height ); } } class Health { int currentHealth, maxHealth; Health ( int maxH ) { maxHealth = maxH; currentHealth = maxHealth; } void update () { currentHealth--; if ( currentHealth <= 0 ) { checkGameOver(); } } void displayHealth () { for (int i = 0; i < currentHealth/10; i++) { int x = 270 - (i*18); int y = 20; image(heart, x, y, heart.width, heart.height); } } } void checkGameOver() { // redraw background image(backgroundImage, 0, 0); noLoop(); fill(255); text("Game Over", width/2, height/2); noFill(); } void checkGameOver(int currH, int maxH) { if (currH >= maxH) { noLoop(); redraw(); text("Game Over", width/2, height/2); } } void checkCollision (FloatingObject[] object, int type) { for (int i = 0; i < object.length; i++) { float d = dist(object[i].x, object[i].y, currentX, currentY + diver.height); // calculate overlap including offset of diver image if (d < rSum) { // if there is an overlap (collision) if (object[i].on == true) { switch(type) { case 1: // points score++; // increase score if hit //pop.trigger(); break; case 2: // damage healthScore.update(); // decrease health if hit grunt.trigger(); break; } } object[i].on = false; // remove object } } } void checkRockCollision ( float x1, float y1 ) { println("x1: " + x1 + " y1: " + y1 + " rad: " + theDiver.rad); noFill(); //stroke(0); beginShape(); int i = 0; boolean insideLeft = insidePolygon(theDiver.x, theDiver.y, polygonLeft); while (true) { boolean intersectLeft = insideLeft || lineIntersectsCircle( polygonLeft[i], polygonLeft[(i+1) % polygonLeft.length], theDiver ); if (intersectLeft && !insideRockLeftFlag) { println("hit on the left"); healthScore.update(); grunt.trigger(); insideRockLeftFlag = true; } else if (!insideLeft && insideRockLeftFlag ) { insideRockLeftFlag = false; } vertex( polygonLeft[i].x, polygonLeft[i].y ); i ++; if ( i % polygonLeft.length == 0 ) break; } endShape(CLOSE); beginShape(); int j = 0; boolean insideRight = insidePolygon(theDiver.x, theDiver.y, polygonRight); while (true) { boolean intersectRight = insideRight || lineIntersectsCircle( polygonRight[j], polygonRight[(j+1) % polygonRight.length], theDiver ); if (intersectRight && !insideRockRightFlag) { println("hit on the right"); healthScore.update(); grunt.trigger(); insideRockRightFlag = true; } else { //insideRockRightFlag = false; } vertex( polygonRight[j].x, polygonRight[j].y ); j ++; if ( j % polygonRight.length == 0 ) break; } endShape(CLOSE); handleBoundaryCollisions( particles[particles.length-1] ); // diver bounce off wall } // really basic collision strategy: // sides of the window are walls // if it hits a wall pull it outside the wall and flip the direction of the velocity // the collisions aren't perfect so we take them down a notch too void handleBoundaryCollisions( Particle p ) { if ( p.position().x() < 0 || p.position().x() > width ) p.setVelocity( -0.9*p.velocity().x(), p.velocity().y(), 0 ); if ( p.position().y() < 0 || p.position().y() > height ) p.setVelocity( p.velocity().x(), -0.9*p.velocity().y(), 0 ); p.moveTo( constrain( p.position().x(), 0, width ), constrain( p.position().y(), 0, height ), 0 ); } boolean lineIntersectsCircle ( Point p1, Point p2, DiverClass c ) { float ldx = p2.x-p1.x; float ldy = p2.y-p1.y; float lk = ldy / ldx; float ld = p1.y - p1.x * lk; float cd2 = c.y - c.x * (-1.0/lk); // parallel line thru circle center float xx = (ld - cd2) / ((-1.0/lk)-lk); float yy = xx * lk + ld; if ( !(xx >= min( p1.x, p2.x ) & xx <= max( p1.x, p2.x ) & yy >= min( p1.y, p2.y ) & yy <= max( p1.y, p2.y )) ) return false; float rr = sqrt((c.x-xx)*(c.x-xx)+(c.y-yy)*(c.y-yy)); return c.rad >= rr; } boolean insidePolygon ( float x, float y, Point[] p) { // aaron steed // http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Programs;action=display;num=1189178826;start=4#4 int i, j, c = 0; for (i = 0, j = p.length-1; i < p.length; j = i++) { if ((((p[i].y <= y) && (y < p[j].y)) || ((p[j].y <= y) && (y < p[i].y))) && (x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)) c = (c+1)%2; } return c == 1; } void rockAreas() { polygonLeft = new Point[15]; polygonLeft[0] = new Point(0, 138); polygonLeft[1] = new Point(10, 180); polygonLeft[2] = new Point(12, 295); polygonLeft[3] = new Point(10, 310); polygonLeft[4] = new Point(25, 355); polygonLeft[5] = new Point(30, 380); polygonLeft[6] = new Point(30, 395); polygonLeft[7] = new Point(35, 405); polygonLeft[8] = new Point(35, 415); polygonLeft[9] = new Point(45, 450); polygonLeft[10] = new Point(40, 465); polygonLeft[11] = new Point(50, 495); polygonLeft[12] = new Point(50, 530); polygonLeft[13] = new Point(35, 600); polygonLeft[14] = new Point(0, 600); polygonRight = new Point[17]; polygonRight[0] = new Point(300, 75); polygonRight[1] = new Point(295, 95); polygonRight[2] = new Point(290, 140); polygonRight[3] = new Point(285, 150); polygonRight[4] = new Point(290, 180); polygonRight[5] = new Point(295, 200); polygonRight[6] = new Point(285, 230); polygonRight[7] = new Point(275, 265); polygonRight[8] = new Point(285, 275); polygonRight[9] = new Point(275, 320); polygonRight[10] = new Point(280, 340); polygonRight[11] = new Point(265, 400); polygonRight[12] = new Point(265, 460); polygonRight[13] = new Point(285, 510); polygonRight[14] = new Point(275, 535); polygonRight[15] = new Point(280, 600); polygonRight[16] = new Point(300, 600); }