/*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);
}