/*"The Party" - Rick Gilliland
There are three balls: Lovely, Grumpy, and Drunky.
Lovely and Grumpy are going out, and Drunky is a
mutual friend. Lovely, however, likes the mouse
quite a bit, and constantly seeks it out. Grumpy,
being a jealous fellow, does not like when Lovely
fraternizes with others. If the mouse and Lovely
get too close, Grumpy gets angry and breaks up the
engagement. Drunky is an odd chap. He has had
far too much to drink this evening. As such, he
stumbles about the room. He can talk some sense
into Grumpy and calm him down if he's near by, and
if the mouse is pressed, Drunky will go seek out
Grumpy. If Drunky walks over to Lovely, however,
Grumpy will get just as mad as if the mouse were
talking to her, and Grumpy will charge his
good friend.
*/
BallA grumpy;
BallB lovely;
BallC drunky;
void setup(){
size(400,400);
background(0);
smooth();
grumpy = new BallA(width/2, height/2, 60);
lovely = new BallB(width/2+80, height/2-20, 60);
drunky = new BallC(width/2-80, height/2-20, 60);
}
void draw(){
background(0);
translate(0,height);
scale(1,-1);
//Check to see if the mouse is near Lovely
if (dist(mouseX, mouseY, lovely.pos.x, 400-lovely.pos.y) < lovely.rad + 10){
grumpy.angry = true;
grumpy.targetMouse = true;
//Check to see if drunky is near lovely
} else if (dist(lovely.pos.x, lovely.pos.y, drunky.pos.x, drunky.pos.y) < drunky.rad*2 +20){
grumpy.angry = true;
grumpy.targetMouse = false;
} else {
grumpy.angry = false;
}
//If drunky is near grumpy, grumpy chills.
if (dist(grumpy.pos.x, grumpy.pos.y, drunky.pos.x, drunky.pos.y) < drunky.rad*2 +20){
grumpy.angry = false;
grumpy.rage = false;
grumpy.anger = 0;
}
collide(grumpy, lovely);
collide(grumpy, drunky);
collide(drunky, lovely);
grumpy.update();
lovely.update();
drunky.update();
}
class Ball{
Vector pos, vel;
float rad, dia;
void wallCheck(){
if( ((0 + rad) > pos.x) || ((width - rad) < pos.x)){
vel.x = -vel.x;
pos.x = constrain(pos.x, 0+rad, width-rad);
}
if(((0 + rad) > pos.y) || ((height - rad) < pos.y)){
vel.y = -vel.y;
pos.y = constrain(pos.y, 0+rad, width-rad);
}
}
void run(){
wallCheck();
pos.add(vel);
vel.mult(.99);
fill(255);
noStroke();
}
void update(){
}
}
class BallC extends Ball{
BallC(float X, float Y, float Dia){
pos = new Vector(X, Y);
vel = new Vector();
dia = Dia;
rad = dia/2;
}
void update(){
run();
stumble();
ellipse(pos.x, pos.y, dia, dia);
}
void stumble(){
if (mousePressed){
vel.add(random(-.2, .2), random(-.2, .2));
Vector temp = new Vector(grumpy.pos.x - pos.x, grumpy.pos.y - pos.y);
temp.normalize();
temp.mult(.05);
vel.add(temp);
} else {
vel.add(new Vector(random(-.3, .3), random(-.3, .3)));
}
}
}
class BallB extends Ball{
BallB(float X, float Y, float Dia){
pos = new Vector(X, Y);
vel = new Vector();
dia = Dia;
rad = dia/2;
}
void update(){
run();
seek();
ellipse(pos.x, pos.y, dia, dia);
}
void seek(){
if(dist(mouseX, mouseY, pos.x, pos.y) > rad/2 ){
Vector temp = new Vector(mouseX - pos.x, (height-mouseY) - pos.y);
temp.normalize();
temp.mult(.01);
vel.add(temp);
vel.add(new Vector(random(-.05, .05), random(-.05, .05)));
}
}
}
class BallA extends Ball{
boolean angry, rage, targetMouse;
float anger;
int rageLocker;
BallA(float X, float Y, float Dia){
pos = new Vector(X, Y);
vel = new Vector();
dia = Dia;
rad = dia/2;
}
void update(){
run();
rageLocker++;
pushMatrix();
if (rageLocker > 90){
if (angry){
rage = true;
translate(random(-5, 5), random(-5, 5));
if (anger < 30){
anger += .25;
} else {
rage();
}
} else if(rage){
rage();
}
}
ellipse(pos.x, pos.y, dia, dia);
popMatrix();
}
void rage(){
rage = false;
if (targetMouse){
vel = new Vector(mouseX - pos.x, (height-mouseY) - pos.y);
} else {
vel = new Vector(drunky.pos.x - pos.x, drunky.pos.y - pos.y);
}
vel.normalize();
vel.mult(grumpy.anger);
anger = 0;
rageLocker = 0;
}
}
void collide(Ball A, Ball B){
float disX = A.pos.x - B.pos.x;
float disY = A.pos.y - B.pos.y;
float disR = A.rad + B.rad;
if(disR*disR > (disX*disX + disY*disY)){ //Cheapest collision check we can do, because this is run often
Vector U1x, U1y, U2x, U2y; //Temp component Vectors
Vector temp = new Vector(disX, disY); //Axis Vector that we project on
//While this is a near mimic of the above if, this is more costly.
//As such, we only run it when we know there is a collision.
float over = (disR - sqrt(disX*disX + disY*disY))*.5; //Find the overlap
temp = temp.unit(); // Unit vector.
A.pos.add(temp.prod(over)); // Push A along the axis to outside of B.
U1x = temp.prod(temp.dot(A.vel)); // Projections
U1y = A.vel.dif(U1x);
temp.mult(-1); // Flip the direction of the axis
B.pos.add(temp.prod(over)); // Push B along the axis to outside of A.
U2x = temp.prod(temp.dot(B.vel));
U2y = B.vel.dif(U2x);
//Final Velocities.
A.vel.set(U2x);
A.vel.add(U1y);
A.vel.mult(.75);
B.vel = new Vector();
B.vel.set(U1x);
B.vel.add(U2y);
B.vel.mult(.75);
/*
Vector temp = new Vector(disX, disY);
temp.mult(.1);
A.vel.add(temp);
temp.mult(-1);
B.vel.add(temp);
*/
}
}
class Vector{
float x, y;
Vector(){
x = 0;
y = 0;
}
Vector(Vector V){
x = V.x;
y = V.y;
}
Vector(float X, float Y){
x = X;
y = Y;
}
void set(Vector V){
x = V.x;
y = V.y;
}
void add(Vector V){
x += V.x;
y += V.y;
}
void add(float X, float Y){
x += X;
y += Y;
}
Vector sum(Vector V){
Vector temp = new Vector(x + V.x, y + V.y);
return temp;
}
void sub(Vector V){
x -= V.x;
y -= V.y;
}
Vector dif(Vector V){
Vector temp = new Vector(x - V.x, y - V.y);
return temp;
}
void mult(float T){
x *= T;
y *= T;
}
Vector prod(float T){
Vector temp = new Vector(x*T, y*T);
return temp;
}
float magni(){
float magnitude = sqrt(x*x+y*y);
return magnitude;
}
Vector unit(){
Vector temp = new Vector(x,y);
temp.mult(1/magni());
return temp;
}
void normalize(){
this.mult(1/magni());
}
float dot(Vector V){
float temp = x*V.x + y*V.y;
return temp;
}
}
Exercise 08: Give three circles unique personalities through their qualities of response. For example, one circle can be frightened of the mouse and another affectionate. One circle can become curious about the mouse when it is pressed, but pay no attention otherwise.