/*Space Tentacles By Zachary MacLeod Oh look, there's a GIANT SPACE HORROR IN FRONT OF YOU Good thing you have a HUGE LASER BEAM Fire it by holding the mouse and WATCH HIM WRITHE Moving the mouse around pilots your ship! */ float frameCount =0; //how many frames has it been running for? float shipX=300; //ship's location float shipY=300; float taper; //how much the tentacles shrink in size float len; //how many segments long are the tentacles float segment; //current segment being drawn float tentangle; //angle of the tentacle segment float wiggles; //how many wiggles (half a sin wave) are in the tentacle float bend; //how much is the tentacle bending float wiggleSize; //how big are the wiggles (more extreme sin wave height) float curl; //how much is the tentacle curling at the tip float wiggleStart; //how far along the sinwave does the tentacle start float ouch; //how bad does it hurt color startColor; //starting color for tentacles color endColor; //ending color for tentacles float laserCharge; //how charged is the laser boolean laser; //is the laser firing float redRat; //ratio used for flashing red void setup() { frameCount=10000f; //start with some offset so the tentacls don't begin linked up size(400, 400); } void draw() { //clear(); background(0); redRat = myLerp(1, 1-pow(frameCount%6f, 0.5)/pow(6, 0.5), ouch); //calculate redRat. This does both the red flashing (based off of frame count), and the //use the same random seed for the stars so they don't fling around randomSeed(666); for (int i=0; i<140; i++) { fill(255); ellipse(random(600), ((random(0.15f)+0.5f)*frameCount)%600, 2, 2); } //back tentacles drawBackTentacles(); //aux tentacles drawAuxTentacles(); //handle the ship shipX=myLerp(shipX, mouseX, 0.1f); shipY=myLerp(shipY, mouseY, 0.1f); float shipAngle=((shipX-mouseX)/300f)*PI/4+PI/2; drawShip(shipX, shipY, shipAngle); //increase frame count frameCount++; //Laser Stuff if (laser) { drawLaser(shipX, shipY, shipAngle); laserCharge=min(laserCharge+1, 100f); if (laserCharge>40f) { ouch=myLerp(ouch, 1f, 0.25f); } } else { laserCharge=max(laserCharge-4, 0f); drawLaserExhaust(shipX, shipY, shipAngle); ouch=myLerp(ouch, 0f, 0.25f); } //Draw The Body drawBody(parx(35)); //front tentacles drawFrontTentacles(); drawEye(200+parx(35), 105, 0.6f); drawEye(140+parx(35), 35, 0.3f); drawEye(260+parx(35), 35, 0.3f); drawEye(40+parx(35), 45, 0.5f); drawEye(360+parx(35), 45, 0.5f); } void drawBackTentacles() { for (int i=0; i<15; i++) { //use a seeded semi-random number generator to pick random values but keep them consistent. Not the best random numbers but its good enough. drawTentacle(i*width/15+parx(5), 40+sin(PI*i/15)*20, //x,y 15, 55+semiSeed(9*i)*15+sin(PI*i/15)*25, //size, length PI/2, myLerp(sinscale(semiSeed(i)*20+90, semiSeed(12*i)*0.1f+0.14f), PI/2-angleToPoint(i*width/20+parx(5), 80+sin(PI*i/20)*30, shipX, shipY), ouch), //angle,bend myLerp(sinscale(semiSeed(3.4*i)*450+550, 5f), 8f, ouch), myLerp(semiSeed(666f*i)*0.7f, semiSeed(666f*i)*0.2f+0.3f, ouch), (frameCount/20f)%TWO_PI+semiSeed(7*i)*1000, //wiggles, wigglesize, wigglestart 0f, color(150, 120*redRat, 140*redRat), color(5, 0, 5*redRat)); //curl, start color, end color } } //draws a single lump of the body void drawBodyLump(float x, float y, float hor, float ver, float colorScale, int isOutline) { //draw the outline first if (isOutline == 1) { stroke(20, 0, 20); strokeWeight(3); fill(20, 0, 20); ellipse(x, y, hor, ver); } else { noStroke(); fill(194*colorScale, 114*redRat*colorScale, 129*redRat*colorScale); ellipse(x, y, hor, ver); if (laserCharge>40) { noStroke(); fill(0, 0, 0, 25); float dir = angleToPoint(x, y, shipX, shipY)-PI; for (int i = 0; i<10; i++) { arc(x, y, hor, ver, dir-PI*(i/10f), dir+PI*(i/10f)); } } } } void drawAuxTentacles() { //leftAux drawTentacle(70+parx(25), 75, //x, y 40, sinscale(352f, 15f)+40, //size, length myLerp(PI/2, PI/1.5, ouch), sinscale(90f, 0.15f), //angle, bend sinscale(134, 2f)+6f, sinscale(134, 0.7f), myLerp((frameCount/18f)%TWO_PI, frameCount/2f%TWO_PI, ouch), //wiggles, wigglesize, wigglestart myLerp(sinscale(203f, 2f), 0, ouch), color(220, 170*redRat, 160*redRat), color(70, 20*redRat, 50*redRat)); //curl, start color, end color //rightAux //slightly different values used to keep them un-synced drawTentacle(330+parx(25), 75, //x, y 40, sinscale(338f, 15f)+40, //size, length myLerp(PI/2, PI/4, ouch), sinscale(-85f, 0.15f), //angle, bend sinscale(154, 2f)+6f, sinscale(164, 0.7f), myLerp((frameCount/17f)%TWO_PI, frameCount/2f%TWO_PI, ouch), //wiggles, wigglesize, wigglestart myLerp(sinscale(184f, -2f), 0, ouch), color(220, 170*redRat, 160*redRat), color(70, 20*redRat, 50*redRat));//curl, start color, end color } void drawFrontTentacles() { drawTentacle(135+parx(35), 125, //x, y 50, myLerp(sinscale(286f, 10f)+30, 40, ouch), //size, length PI/2, myLerp(sinscale(93f, 0.15f), horrorToShip()/4, ouch), //angle, bend myLerp(sinscale(164, 2f)+6f, 10f, ouch), sinscale(74, 0.9f), myLerp((frameCount/20f)%TWO_PI, frameCount%TWO_PI, ouch), //wiggles, wigglesize, wigglestart myLerp(sinscale(194f, 1.8f), 0, ouch), color(240, 170*redRat, 160*redRat), color(120, 20*redRat, 90*redRat));//curl, start color, end color drawTentacle(265+parx(35), 125, //x, y 50, myLerp(sinscale(294f, 10f)+30, 40, ouch), //size, length PI/2, myLerp(sinscale(-82f, -0.15f), -horrorToShip()/4, ouch), //angle, bend myLerp(sinscale(177, 2f)+6f, 10f, ouch), myLerp(sinscale(94, 0.9f), 0.4f, ouch), myLerp((frameCount/21f)%TWO_PI, frameCount%TWO_PI, ouch), //wiggles, wigglesize, wigglestart myLerp(sinscale(174f, -1.8f), 0, ouch), color(240, 170*redRat, 160*redRat), color(120, 20*redRat, 90*redRat));//curl, start color, end color } void drawBody(float x) { //uses a for loop, to draw the outline first. for (int i=0; i<=1; i++) { drawBodyLump(200+x, 80, 220, 140, 1f, 1-i); drawBodyLump(85+x, 20, 80, 45, 1f, 1-i); drawBodyLump(315+x, 20, 80, 45, 1f, 1-i); drawBodyLump(10+x, 30, 150, 125, 0.9f, 1-i); drawBodyLump(390+x, 30, 150, 125, 0.9f, 1-i); drawBodyLump(90+x, 30, 70, 95, 0.9f, 1-i); drawBodyLump(310+x, 30, 70, 95, 0.9f, 1-i); drawBodyLump(200+x, -50, 360, 220, 0.8f, 1-i); } } void drawEye(float x, float y, float scale) { noStroke(); //draw sclera for (int i=0; i<50; i++) { fill(lerpColor(color(50, 48*redRat, 38*redRat), color(139, 244*redRat, 128*redRat), i/50f)); ellipse(x, y, pow(50f-i, 0.2f)/pow(50f, 0.2f)*130f*scale, pow(50f-i, 0.4f)/pow(50f, 0.4f)*130f*scale); } fill(255, 64); ellipse(x-130f/4f*scale, y-130f/9f*scale, 130/5f*scale, 130/6f*scale); //white reflection ellipse(x-130f/4f*scale, y-130f/8f*scale, 130/5f*scale, 130/4f*scale); //white reflection drawIris(x+18*scale*cos(myLerp(angleToPoint(x, y, shipX, shipY), (frameCount+x) % TWO_PI, ouch)), y+10*sin(myLerp(angleToPoint(x, y, shipX, shipY), (frameCount+x) % TWO_PI, ouch)), 40*scale, 80*scale, myLerp(24, 4, ouch)*scale); //draw lower inner Eyelid, same as outer eyelid but darker + squashed drawEyelid(x, y, 130*scale, 110*scale, myLerp(0.4f, sinscale(4f, 0.4f)+0.8f, ouch), 10, PI/2, PI/3+sinscale(200, PI/40f), color(154, 64*redRat, 109*redRat)); //draw lower outer eyelid drawEyelid(x, y, 130*scale, 130*scale, myLerp(0.4f, sinscale(4f, 0.4f)+0.8f, ouch), 10, PI/2, PI/3+sinscale(200, PI/40f), color(204, 124*redRat, 139*redRat)); //draw upper inner Eyelid drawEyelid(x, y, 130*scale, 110*scale, myLerp(0.4f, 1.2f, ouch), 10, -PI/2, myLerp(PI/2+sinscale(140, PI/40f), PI/2f-PI/10f, ouch), color(154, 64*redRat, 109*redRat)); //draw upper outer Eyelid drawEyelid(x, y, 130*scale, 130*scale, myLerp(0.4f, 1.2f, ouch), 10, -PI/2, myLerp(PI/2+sinscale(140, PI/40f), PI/2f-PI/10f, ouch), color(204, 124*redRat, 139*redRat)); } void drawIris(float x, float y, float hor, float ver, float iris) { fill(171, 244*redRat, 34*redRat); strokeWeight(3); stroke(84, 130*redRat, 34*redRat); arc(x+hor/5.3f, y, hor, ver, PI/2+PI/8, TWO_PI-PI/2-PI/8); arc(x-hor/5.3f, y, hor, ver, -(PI/2-PI/8), PI/2-PI/8); fill(41, 64*redRat, 14*redRat); ellipse(x, y, iris, iris); } void drawEyelid(float x, float y, float hor, float ver, float curve, float smoothness, float baseAngle, float halfArc, color Color) { for (int i=0; i<smoothness+1; i++) { float startRat = pow(i/smoothness, curve); float endRat = pow(1-(i/smoothness), curve); fill(Color); strokeWeight(2); stroke(0, 0, 0, 128); arc(x, y, hor, ver, baseAngle-halfArc*endRat, baseAngle+halfArc*startRat); } } void mousePressed() { laser=true; } void mouseReleased() { laser=false; } //after you release mouse key void drawLaserExhaust(float x, float y, float angle) { if (laserCharge>0f) { strokeWeight(2f); stroke(200, 255, 255, 30); fill(235, 255, 255, 20); for (float i =0; i<min(40f, laserCharge); i+=7) { float rat = pow(0.9f, laserCharge-i); rat*=1-(min(laserCharge-80f, 0f)); ellipse(x, y, (10+i/25)*rat, (10+i/25)*rat); } if (laserCharge>40f) { float rat = pow(1-(70f-min(70f, laserCharge))/(30f), -1f); fill(205, 255, 255, 140*1/rat); noStroke(); quad(x+rotX(-4, 0, angle), y+rotY(-4, 0, angle), x+rotX(4, 0, angle), y+rotY(4, 0, angle), x+rotX(4+26*rat, -800, angle), y+rotY(4+26*rat, -800, angle), x+rotX(-4-26*rat, -800, angle), y+rotY(-4-26*rat, -800, angle)); for (int i=0; i<25; i++) { fill(205, 255, 255, 15); quad(x+rotX(-4, 0, angle), y+rotY(-4, 0, angle), x+rotX(4, 0, angle), y+rotY(4, 0, angle), x+rotX(4+(26+i*3)*rat, -800, angle), y+rotY(4+(26+i*3)*rat, -800, angle), x+rotX(-4-(26+i*3)*rat, -800, angle), y+rotY(-4-(26+i*3)*rat, -800, angle)); } } } } //laser including while charging void drawLaser(float x, float y, float angle) { strokeWeight(2f); stroke(200, 255, 255, 128); fill(235, 255, 255, 60); for (float i =0; i<min(40f, laserCharge); i+=7) { float rat = pow(0.9f, laserCharge-i); rat*=1-(min(laserCharge-80f, 0f)); ellipse(x, y, (40+i/2)*rat, (40+i/2)*rat); } if (laserCharge>40f) { float rat = pow(1-(70f-min(70f, laserCharge))/(30f), -2f); fill(205, 255, 255, 255*1/rat); noStroke(); quad(x+rotX(-4, 0, angle), y+rotY(-4, 0, angle), x+rotX(4, 0, angle), y+rotY(4, 0, angle), x+rotX(4+26*rat, -800, angle), y+rotY(4+26*rat, -800, angle), x+rotX(-4-26*rat, -800, angle), y+rotY(-4-26*rat, -800, angle)); for (int i=0; i<25; i++) { fill(205, 255, 255, 15); quad(x+rotX(-4, 0, angle), y+rotY(-4, 0, angle), x+rotX(4, 0, angle), y+rotY(4, 0, angle), x+rotX(4+(26+i*3)*rat, -800, angle), y+rotY(4+(26+i*3)*rat, -800, angle), x+rotX(-4-(26+i*3)*rat, -800, angle), y+rotY(-4-(26+i*3)*rat, -800, angle)); } for (int i=0; i<15; i++) { fill(255, 255, 255, 35); quad(x+rotX(-4, 0, angle), y+rotY(-4, 0, angle), x+rotX(4, 0, angle), y+rotY(4, 0, angle), x+rotX(4+(26-i*3)*rat, -800, angle), y+rotY(4+(26-i*3)*rat, -800, angle), x+rotX(-4-(26-i*3)*rat, -800, angle), y+rotY(-4-(26-i*3)*rat, -800, angle)); } int maxRays = 20; for (float i=0; i<maxRays; i++) { strokeWeight(1f); stroke(255, 255, 255, 53); float arc = TWO_PI/maxRays; float startRad = arc*i+frameCount*(PI/24f); float midRad = arc*(i+0.5f)+frameCount*(PI/24f); float endRad = arc*(i+1)+frameCount*(PI/24f); float startX = 50*cos(startRad); float startY = 20*sin(startRad); float midX = 250*cos(midRad); float midY = -100-20*cos(midRad); float endX = 50*cos(endRad); float endY = 20*sin(endRad); triangle(x+rotX(startX, startY, angle), y+rotY(startX, startY, angle), x+rotX(midX, midY, angle), y+rotY(midX, midY, angle), x+rotX(endX, endY, angle), y+rotY(endX, endY, angle)); } } } //the ship void drawShip(float x, float y, float angle) { strokeWeight(2f); stroke(0, 0, 0, 128); fill(118, 125, 164); quad(x+rotX(7, 12, angle), y+rotY(7, 12, angle), x+rotX(19, 12, angle), y+rotY(19, 12, angle), x+rotX(19, 39, angle), y+rotY(19, 39, angle), x+rotX(7, 39, angle), y+rotY(7, 39, angle)); //rThruster quad(x+rotX(-19, 12, angle), y+rotY(-19, 12, angle), x+rotX(-7, 12, angle), y+rotY(-7, 12, angle), x+rotX(-7, 39, angle), y+rotY(-7, 39, angle), x+rotX(-19, 39, angle), y+rotY(-19, 39, angle)); //lThruster fill(128, 138, 194); quad(x+rotX(-12, 0, angle), y+rotY(-12, 0, angle), x+rotX(-12, 34, angle), y+rotY(-12, 34, angle), x+rotX(12, 34, angle), y+rotY(12, 34, angle), x+rotX(12, 0, angle), y+rotY(12, 0, angle)); //backBod fill(108, 105, 134); quad(x+rotX(-7, -12, angle), y+rotY(-7, -12, angle), x+rotX(7, -12, angle), y+rotY(7, -12, angle), x+rotX(7, 12, angle), y+rotY(7, 12, angle), x+rotX(-7, 12, angle), y+rotY(-7, 12, angle)); //frontBod fill(94, 94, 158); quad(x+rotX(-7, -12, angle), y+rotY(-7, -12, angle), x+rotX(7, -12, angle), y+rotY(7, -12, angle), x+rotX(2, -20, angle), y+rotY(2, -20, angle), x+rotX(-2, -20, angle), y+rotY(-2, -20, angle)); //cone fill(0, 120, 160); quad(x+rotX(-12, 0, angle), y+rotY(-12, 0, angle), x+rotX(-43, 36, angle), y+rotY(-43, 36, angle), x+rotX(-16, 34, angle), y+rotY(-16, 34, angle), x+rotX(-9, 12, angle), y+rotY(-9, 12, angle)); //lWing quad(x+rotX(12, 0, angle), y+rotY(12, 0, angle), x+rotX(43, 36, angle), y+rotY(43, 36, angle), x+rotX(16, 34, angle), y+rotY(16, 34, angle), x+rotX(9, 12, angle), y+rotY(9, 12, angle)); //rWing quad(x+rotX(-16, 12, angle), y+rotY(-16, 12, angle), x+rotX(-12, 12, angle), y+rotY(-12, 12, angle), x+rotX(-12, 22, angle), y+rotY(-12, 22, angle), x+rotX(-16, 22, angle), y+rotY(-16, 22, angle)); //lDecal quad(x+rotX(16, 12, angle), y+rotY(16, 12, angle), x+rotX(12, 12, angle), y+rotY(12, 12, angle), x+rotX(12, 22, angle), y+rotY(12, 22, angle), x+rotX(16, 22, angle), y+rotY(16, 22, angle)); //rDecal stroke(200, 255, 255, 128); fill(0, 255, 255); quad(x+rotX(-2, -7, angle), y+rotY(-2, -7, angle), x+rotX(2, -7, angle), y+rotY(2, -7, angle), x+rotX(2, 29, angle), y+rotY(2, 29, angle), x+rotX(-2, 29, angle), y+rotY(-2, 29, angle)); //Power } //rotate a point around 0,0 by angle float rotX(float x, float y, float angle) { return(x*sin(angle)-y*cos(angle)); } float rotY(float x, float y, float angle) { return(y*sin(angle)+x*cos(angle)); } //draws the base of the tentacle by setting a bunch of global variables then starting a recursive method void drawTentacle(float x, float y, float size, float _len, float _angle, float _bend, float _wiggles, float _wiggleSize, float _wiggleStart, float _curl, color _startColor, color _endColor) { len = _len; taper = size/len; tentangle = _angle; bend = PI*_bend; wiggles = _wiggles; wiggleSize = PI*_wiggleSize; wiggleStart = _wiggleStart%TWO_PI; curl = PI*_curl; startColor = _startColor; endColor= _endColor; segment=0; drawTentacle2(x, y, size); } //recursive method that makes a tentacle draw itself and all following segments void drawTentacle2(float x, float y, float size) { //saves the size and color of the tentacle, to draw them at the end float oldsize = size; color myColor = lerpColor(startColor, endColor, segment/len); noStroke(); //draw the "outline" first //fill(myColor*0.4f); fill(0); ellipse(x, y, oldsize+3, oldsize+3); //prepare the next tentacle before drawing self if (segment < len && size>5) { //check if theres room for more first though float angleOff = sin(segment/len*(PI*wiggles)+wiggleStart)*pow(segment/len, 1.1f)*wiggleSize ; //calculate angleoffset from wigglign tentangle+=(bend/len);//add the turn rate, as a ratio of the tentacle's length tentangle += (curl/len)*(pow(max(0, segment/len), 2.5f)*4); //add the curl rate, which is strong but comes in later size-=taper; //decrease size of the next segment segment++; //keep track of semgents created drawTentacle2(x+cos(tentangle+angleOff)*min(10, size/3), y+sin(tentangle+angleOff)*min(10, size/3), size); //draw its child before itself. } //draw the circle at the end, thus drawing all its children On Top. fill(myColor); ellipse(x, y, oldsize, oldsize); } //gets angle between two points float angleToPoint(float x1, float y1, float x2, float y2) { float dx = x2-x1; float dy = y2-y1; float angle = atan2(dy, dx); if (angle<0) { angle=abs(angle); } //else{angle = TWO_PI-angle;} return angle; } //gets the angle from the space horror to the ship float horrorToShip() { return angleToPoint(300+parx(35f), 140, shipX, shipY); } //the lerp function wasnt covered in the textbooks, so I figured its not allowed. Its a simple function though so I made one of my own. float myLerp(float tempMin, float tempMax, float rat) { rat = max(rat, 0); rat = min(rat, 1); return (tempMax-tempMin)*rat+tempMin; } float semiSeed(float val) { //gets a semi-random number using val as a seed return ((PI*val)%0.2*10f)-1; } //takes in a value, throws it on a sine wave with a length and height. float sinscale(float val, float wavelen, float waveheight) { return sin(val*(PI/wavelen))*waveheight; } float sinscale(float wavelen, float waveheight) { return sin(frameCount*(PI/wavelen))*waveheight; } //X Parallax based on mouse float parx(float val) { return (mouseX/float(width)*val)-(val/2f); }