/* Urchin Class Defines behaviour and appearance of the little urchins. The collision adjustment here is a bit of a hack-job. But for this simple sketch I didn't want to mess around with anything more complex. */ public class Urchin { //Declare properties. These properties are available to each individual Urchin. float rad = 10; //Radius of body float mass; //Mass of urchin int id; //Physics VerletParticle2D pos; Vec2D lastpos; Vec2D vel; Vec2D acc; //Eye widths float eye1; float eye2; float blinkChance = 0.01; int blinkLength = 15; int blinkCount = 0; boolean blinking = false; boolean blinkRight = false; boolean blinkLeft = false; //Rotation float rot = 0; //Is he stuck to the mouse?? boolean stuck = false; int stuckCount = 0; VerletParticle2D stickTarget; boolean col = false; //Constructor Class Urchin() { pos = new VerletParticle2D(0,0); vel = new Vec2D(0,0); acc = new Vec2D(0,0); }; void init() { eye1 = random(3,rad * 1.2); eye2 = random(3,rad * 1.2); //eye1 = 5; //eye2 = 5; mass = pow(PI * rad, 2) * 2; lastpos = pos.copy(); }; //update method - performs physics operations, etc. void update() { lastpos = pos.copy(); //Blinking! if (blinking) { blinkCount ++; if (blinkCount > blinkLength) { //Stop Blinking blinkCount = 0; blinkLeft = false; blinkRight = false; blinking = false; }; }; if (random(1) < blinkChance) { blinking = true; if ( random(1) > 0.5) blinkLeft = true; if ( random(1) > 0.5) blinkRight = true; }; //Physics updates if (stuck) { stuckCount ++; } else { stuckCount = 0; vel.addSelf(acc); vel.addSelf(gravity); //vel.scaleSelf(0.99); if (abs(vel.y) < 1) { Vec2D ma = mouse.sub(pos); vel.addSelf(ma.scale(0.004).x, 0); }; pos.addSelf(vel); acc.clear(); collide(); if (pos.y > height - rad) { pos.y = height - rad; vel.y *= -wallFriction.y; }; if (pos.x > width - rad) { pos.x = width - rad; vel.x *= -wallFriction.x; }; if (pos.x < rad) { pos.x = rad; vel.x *= -wallFriction.x; }; //JUMP! if (random(100) < 2) vel.jitter(0,10); if (abs(mouse.distanceTo(pos)) < 50) { stickIt(this); }; }; }; void collide() { for (int i = id + 1; i < urchinNum; i++) { Urchin t = (Urchin) urchins.get(i); //Are they moving towards each other? //This is a reasonably light weight calculation and is good to pare down the list. if ( (t.pos.x - pos.x) * (vel.x - t.vel.x) + (t.pos.y - pos.y) * (vel.y - t.vel.y) > 0) { //If they are heading together, check to see if they are actually touching. //Find the distance between this urchin and the other float d = pos.distanceTo(t.pos); float minDist = rad + t.rad; //If the distance is smaller than the sum of the radii, there's a hit if (abs(d) < minDist) { float bleed = minDist - abs(d); //During a hit there is a change in angle and a transfer of momentum float momx1 = vel.x * mass; float momy1 = vel.y * mass; float momy2 = t.vel.y * t.mass; float momx2 = t.vel.x * t.mass; //Find the angle between the two velocity vectors float angle = vel.angleBetween(t.vel, true); //calculate cos and sin float sine = sin(angle); float cosine = cos(angle); //Adjust the velocities of the balls vel.x = (momx2 * cosine)/mass; vel.y = (momy2 * sine)/mass; t.vel.x = (momx1 * cosine)/t.mass; t.vel.y = (momy1 * sine)/t.mass; //Sticking occurs becuase our script doesn't run continuously and might miss the point of contact. This overlap causes the balls //To get stuck in a never-ending loop of togetherness (awww.) pos.addSelf(vel.scale(4)); t.pos.addSelf(t.vel.scale(4)); }; }; }; }; //draw method - renders the urchin to screen void draw() { if (stuck) { //Draw the web line stroke(0,0,0,100); //Vec2D anchor = pos.interpolateTo(stickTarget, stuckCount/100); line(stickTarget.x, stickTarget.y, pos.x, pos.y); }; //Body noStroke(); fill(0); if (col) fill(240); ellipse(pos.x, pos.y,rad * 2, rad * 2); for (int j = 0; j < 2; j++) { imageMode(CENTER); image(mote3, lastpos.x - random(-rad/2, rad/2), lastpos.y - random(-rad/2, rad/2), rad * 2, rad * 2); }; //Rays col = false; strokeWeight(random(1,2)); stroke(0,0,0,random(200,215)); for (int i = 0; i < 20; i++) { pushMatrix(); translate(pos.x, pos.y); rotate(random(6.28)); line(0,0,0,random(rad,rad*1.5)); popMatrix(); }; //Eyes //fill(255,0,0); pushMatrix(); translate(pos.x, pos.y); rotate(rot); rot += vel.x/10; float r = random(1,rad); noStroke(); if (!blinkLeft) { image(eye, -rad * 0.4, 0, eye1 * 2, eye1 * 2); } if (!blinkRight) { image(eye, rad * 0.4, 0, eye2, eye2); }; popMatrix(); }; };