Bubble popping animation

Hello!

Here is a simple sketch showing the use of cos and sin (taken from this forum).

This way you get a circle around a point.

PointCircle[] points; //might want to use a list (ArrayList)

void setup() {
  size(1300, 600);

  final float RADIUS = 80.8;

  final float ANGLE_INCREMENT = TWO_PI/10; 
  //2PI would only shoot down, PI is up down, 
  //halfPi is in 4 dirs and so on
  //you can also just use some random number like 0.3256
  //but try keeping it within 0-2PI

  points = new PointCircle[0];

  for (int i=0; i<10; i++) {
    float angle=i*ANGLE_INCREMENT; 
    float CENTER_X=width/2;
    float CENTER_Y=height/2;
    // make a new bullet
    PointCircle newBullet = new PointCircle( cos(angle)*RADIUS+CENTER_X, sin(angle)*RADIUS+CENTER_Y);
    // append it to array
    points = (PointCircle[]) append(points, newBullet);
  }
}

void draw() {
  background(255);

  for (int i = 0; i < points.length; i++) {
    points[i].display();
  }//for
}////func

// ================================================================================

class PointCircle { 
  // Bullet

  float posX, posY;

  PointCircle (float x, float y ) {
    posX = x;
    posY = y;
  }

  void display() {
    fill(255, 0, 0); 
    ellipse(posX, posY, 
      3, 3);
  }
  //
}//class
//

Here is another explosion based on the cos sin sketch (also from this forum).
It’s also with fading.


Bullet[] bullets; //might want to use a list (ArrayList)

void setup() {
  size(1300, 600);

  final float RADIUS = 1.8;

  final float ANGLE_INCREMENT = TWO_PI/10; 
  //2PI would only shoot down, PI is up down, 
  //halfPi is in 4 dirs and so on
  //you can also just use some random number like 0.3256
  //but try keeping it within 0-2PI

  bullets = new Bullet[0];

  for (int i=0; i<10; i++) {
    float angle=i*ANGLE_INCREMENT; 
    float CENTER_X=width/2;
    float CENTER_Y=height/2;
    // make a new bullet
    Bullet newBullet = new Bullet(
      cos(angle)*RADIUS+CENTER_X, sin(angle)*RADIUS+CENTER_Y, 
      cos(angle)*0.8, sin(angle)*0.8);
    // append it to array
    bullets = (Bullet[]) append(bullets, newBullet);
  }
}

void draw() {
  background(255);

  for (int i = 0; i < bullets.length; i++) {
    bullets[i].display();
    bullets[i].move();
  }//for
}////func

// =======================================================================

class Bullet { 
  // Bullet

  float posX, posY;
  float dirX;
  float dirY;

  int fade = 255; 

  Bullet (float x, float y, 
    float dx, float dy) {
    posX = x;
    posY = y;
    dirX = dx;
    dirY = dy;
  }

  void display() {
    fill(255, 0, 0, fade); 
    noStroke();
    ellipse(posX, posY, 
      3, 3);
  }

  void move() {
    posX+=dirX;
    posY+=dirY;
    fade--;
  }
  //
}//class
//

Please note, that it has a class Bullet which represents one circle (shrapnel) of the explosion.

On sketch level (outside the class) is an array of that class.

For your sketch that means that when a ball explodes (and there could be multiple balls with multiple explosions) you start a explosion like in this sketch.

So best when each ball has an array bullets inside it.

You then need the class Bullet.


Bubble b1;

void setup() {
  size(640, 360);
  b1 = new Bubble(50);
}

void draw() {
  background(0);
  b1.all();
}

void mousePressed() {
  if (b1.over()) {
    b1.fall();
  }
}

//====================================================
//Bubble class tab

class Bubble {

  // constants: these are values that state can have (must be unique (0,1,2...))
  final int RISE    = 0;   // the word final makes them constants 
  final int FALL    = 1; 
  final int RESTART = 2; 
  // variable: current value of the state
  int state=RISE;  // not a state of the program, but the state of the ball

  float tempSize;
  float w;
  float h;
  boolean move = true;
  float maxW;
  float maxH;
  float minW;
  float minH;
  float wspeed;
  float hspeed;
  float riseY = random(0.5, 2);
  float riseStart;
  float start = random(0, 640);
  float gravity = 0.5;

  Bullet[] bullets; //might want to use a list (ArrayList)


  Bubble(float tempSize) {
    w = tempSize;
    h = tempSize;
    riseStart = tempSize + height;

    maxW = w*1.1+random(0, maxW/2);
    maxH = h*1.1+random(0, maxH/1);
    minW = w/1.1;
    minH = h/1.1;
    wspeed = w/100;
    hspeed = h/75;
  }

  void all() {
    // all (or script()) 

    print("Y ", mouseY);
    print("  RS ", b1.riseStart);
    print("  X ", mouseX);
    println("  Start", b1.start);

    switch(state) {

    case RISE: 
      show(); 
      move();
      top(); 
      break; 

    case FALL: 
      riseY = riseY - gravity;
      riseStart = riseStart - riseY;
      ellipse(start, riseStart, 
        3, 3); // new small ball
      showExplosion();
      if (riseStart>height+133) {  // lower screen border
        state = RESTART;
        riseY = 5;
      }
      break; 

    case RESTART: 
      // restart 
      state = RISE; 
      riseStart = tempSize + height+100;
      start = random(w, width-w);
      break;

    default:
      // Error 
      println("Error. Unknown state ");
      exit();    
      break; 
      //
    }//switch 
    //
  } // method 

  void show() {
    //noFill();
    //stroke(255);
    fill(255, 0, 0); // RED 
    noStroke(); 
    strokeWeight(2);
    ellipse (start, riseStart, 
      w, h);

    if (move) {
      w = w + wspeed;

      if ((w > maxW) || (w < minW)) { // Makes bubble wobble.
        wspeed = wspeed * -1;
      }

      if (move) {
        h = h + hspeed;
      }
      if ((h > maxH) || (h < minH)) { // Makes bubble wobble.
        hspeed = hspeed * -1;
      }
    }
  }

  void move() {
    riseStart = riseStart - riseY;
  }

  void top() {
    if (riseStart < tempSize - tempSize -100) { 
      state=RESTART;
    }
  }

  void fall() {
    state=FALL;
    explode();
  }

  boolean over() {
    // returns true or false; true when mouse is on balloon.
    // You had this in mousePressed before. 
    return 
      (mouseX < start + w/2) && 
      (mouseX > start - w/2) &&
      (mouseY < riseStart + h/2) && 
      (mouseY > riseStart - h/2);
  }

  void showExplosion() {
    for (int i = 0; i < bullets.length; i++) {
      bullets[i].display();
      bullets[i].move();
    }//for
  }

  void explode() {

    final float RADIUS = 1.8;

    final float ANGLE_INCREMENT = TWO_PI/10; 
    //2PI would only shoot down, PI is up down, 
    //halfPi is in 4 dirs and so on
    //you can also just use some random number like 0.3256
    //but try keeping it within 0-2PI

    // full reset 
    bullets = new Bullet[0];

    for (int i=0; i<10; i++) {
      float angle=i*ANGLE_INCREMENT; 
      // make a new bullet at CURRENT POSITION : start, riseStart
      Bullet newBullet = new Bullet(
        cos(angle)*RADIUS+start, sin(angle)*RADIUS+riseStart, 
        cos(angle)*0.8, sin(angle)*0.8);
      // append it to array
      bullets = (Bullet[]) append(bullets, newBullet);
    }
  }
}//class

// ================================================================================

class Bullet { 
  // Bullet

  float posX, posY;
  float dirX;
  float dirY;

  int fade = 255; 

  Bullet (float x, float y, 
    float dx, float dy) {
    posX = x;
    posY = y;
    dirX = dx;
    dirY = dy;
  }

  void display() {
    fill(255, 0, 0, fade); 
    noStroke();
    ellipse(posX, posY, 
      3, 3);
  }

  void move() {
    posX+=dirX;
    posY+=dirY;
    fade-=1;
  }
  //
}//class
//


Chrisir

(I also showed you an explosion above some weeks ago)

2 Likes

Me and my son looked at this tonight.
We have been taking baby steps and much of this code is way beyond our level as we follow the “Learning Processing” book and tutorials by Daniel Shiffman. We sertanly notice progress by the fact we are actually seeing things in the code examples more now than before!. :joy:
Really cool! :slight_smile:

There is some things we couldn’t figure out.
Our bubble is made with variables all along so everything connects. This is so we can set it to a different size and the wobble will adapt.
When we tries to do this with the “bullets” (now renamed fallouts in the code) we cant figure out how to change this.

We changed the “final float RADIOUS = 1,8” to a variable called “rad” that is based on the bubble dimeter/2. This worked to make the fallout start from the bubble edge :slight_smile:

Then we want to be able to change the number of fallouts depending on the size of the bubble but this we couldn’t find.
The number seems to be hardcoded here:
final float ANGLE_INCREMENT = TWO_PI/10;
and
for (int i=0; i<10; i++) {
I tried using fallout.lenght and “i” but it didn’t work in this case?

We also want to change how far and how fast the fallout moves. We want to have it move a bit less and maybe try some small random here. Could find where to do this?
Is it possible to create the dots a little more random around the circle? just some pixels? So it doesn’t look so perfect :slight_smile:

Then we cant figure out this:

class Fallout { 
  // Bullet

  float posX, posY; **// pos and dir variables crated, no values..**
  float dirX;
  float dirY;

  int fade = 255; 

  Fallout (float x, float y, **// x,y,dx and dy variables created, where do they get there values from?**
    float dx, float dy) {
    posX = x; **// variables with values taken from somewhere and used where? Now getting what value** 
    posY = y;
    dirX = dx;
    dirY = dy;

Also some small question marks.

  • Why is riseY reminding the program of its original values in FALL already and not in RESTART?
  • The default state? when does this appear? And the exit() line. Does it close the viewer?

Played around with it some more and this is what I came up with…
Still cant figure out a lot of the above…

Bubble b1;

void setup() {
  size(640, 360);
  b1 = new Bubble(50);
}

void draw() {
  background(0);
  b1.all();
}

void mousePressed() {
  if (b1.over()) {
    b1.fall();
  }
}




class Bubble {

  // constants: these are values that state can have (must be unique (0,1,2...))
  final int RISE    = 0;   // the word final makes them constants 
  final int FALL    = 1; 
  final int RESTART = 2; 
  // variable: current value of the state
  int state=RISE;  // not a state of the program, but the state of the ball

  float tempSize;
  float w;
  float h;
  boolean move = true;
  float maxW;
  float maxH;
  float minW;
  float minH;
  float wspeed;
  float hspeed;
  float riseY = random(0.8, 3);
  float riseStart;
  float start = random(0, 640);
  float gravity = 0.3;
  float rad;

  Fallout[] fallouts; //might want to use a list (ArrayList)


  Bubble(float tempSize) { // <----------------- Bubble takes value from tab 1 and makes tempSize
    
    w = tempSize;
    h = tempSize;
    riseStart = tempSize + height;

    rad=tempSize/2;
    maxW = w*1.1+random(0, maxW/2);
    maxH = h*1.1+random(0, maxH/1);
    minW = w/1.1;
    minH = h/1.1;
    wspeed = w/100;
    hspeed = h/75;

    //println(tempSize);
  }

  void all() {
    stroke(255,255);
    // all (or script()) 

    //print("Y ", mouseY);
    //print("  RS ", b1.riseStart);
    //print("  X ", mouseX);
    //println("  Start", b1.start);

    switch(state) {

    case RISE: 
      show(); 
      move();
      top(); 
      break; 

    case FALL: 
      riseY = riseY - gravity;
      riseStart = riseStart - riseY;
      ellipse(start, riseStart, 
        3, 3); // new small ball
      showExplosion();
      if (riseStart>height+133) {  // lower screen border
        state = RESTART;
        riseY = random(0.8, 3); // Why do we put this hera and not in case RESTART?
      }
      break; 

    case RESTART: 
      // restart 
      state = RISE; 
      riseStart = tempSize + height+100;
      start = random(w, width-w);
      break;

    default: // What does this state do? When does it run?
      // Error 
      println("Error. Unknown state ");
      exit();    
      break; 
      //
    }//switch 
    //
  } // method 

  void show() {
    //noFill();
    //stroke(255);
    noFill();
    stroke(255,255); 
    strokeWeight(1);
    ellipse (start, riseStart, 
      w, h);

    if (move) {
      w = w + wspeed;

      if ((w > maxW) || (w < minW)) { // Makes bubble wobble.
        wspeed = wspeed * -1;
      }

      if (move) {
        h = h + hspeed;
      }
      if ((h > maxH) || (h < minH)) { // Makes bubble wobble.
        hspeed = hspeed * -1;
      }
    }
  }

  void move() {
    riseStart = riseStart - riseY;
  }

  void top() {
    if (riseStart < tempSize - tempSize -100) { 
      state=RESTART;
    }
  }

  void fall() {
    state=FALL;
    explode();
  }

  boolean over() {
    // returns true or false; true when mouse is on balloon.
    // You had this in mousePressed before. 
    return 
      (mouseX < start + w/2) && 
      (mouseX > start - w/2) &&
      (mouseY < riseStart + h/2) && 
      (mouseY > riseStart - h/2);
  }

  void showExplosion() {
    for (int i = 0; i < fallouts.length; i++) {
      fallouts[i].display();
      fallouts[i].move();
    }//for
  }

  void explode() {

    final float RADIUS = rad;
    println(rad);
    final float ANGLE_INCREMENT = TWO_PI/15; 
    //2PI would only shoot down, PI is up down, 
    //halfPi is in 4 dirs and so on
    //you can also just use some random number like 0.3256
    //but try keeping it within 0-2PI

    // full reset 
    fallouts = new Fallout[0];

    for (int i=0; i<15; i++) {
      float angle=i*ANGLE_INCREMENT; 
      // make a new bullet at CURRENT POSITION : start, riseStart
      Fallout newFallout = new Fallout(
        cos(angle)*RADIUS+start, sin(angle)*RADIUS+riseStart, 
        cos(angle)*0.8, sin(angle)*0.8);
      // append it to array
      fallouts = (Fallout[]) append(fallouts, newFallout);
    }
  }
}//class




class Fallout { 
  // Bullet

  float posX, posY;
  float dirX;
  float dirY;

  int fade = 255; 

  Fallout (float x, float y, 
    float dx, float dy) {
    posX = x;
    posY = y;
    dirX = dx;
    dirY = dy;
  }

  void display() {
    fill(255, fade); 
    noStroke();
    ellipse(posX, posY, 
      1, 1);
  }

  void move() {
    posX+=random(-dirX/5,dirX);
    posY+=random(dirY,-dirY/2);
    fade-=20;
  }
  //
}//class

It’s in the class Bullet, method display() : the ellipse command.

In the method explode(), note that new Bullet (explosion shrapnel or splinters) is generated with 4 parameters: the first two are the position (around the bubble) the second two is the speed of the shrapnel (away from the bubble).

I think replace 10 (in both places) by 20 or whatever.

Where you initiate the 10 bullets - the 2nd two parameters (number 3 and 4) are the speed

default belongs to switch(). It’s doing the same as the else in if else…: it takes care of the cases we forgot. In our case this would apply if state had a value that we didn’t expect. This would be an error. The exit() would terminate the program. But it’s necessary to check whether state has a wrong value and to register this immediately and terminate the program.

Maybe you should read the tutorial on objects. In this part (named the constructor) in the class we initiate a new Bullet. It gets its values from the parameters of this specific function (the constructor) and these are from the parameters copied into the bullet properties. That’s how objects (the cookies) are made from the class (the cookie maker).
You call this part here:

      // make a new bullet at CURRENT POSITION : start, riseStart
      Fallout newFallout = new Fallout(
        cos(angle)*RADIUS+start, sin(angle)*RADIUS+riseStart, 
        cos(angle)*0.8, sin(angle)*0.8);
2 Likes

Hi all. Here is some simple code slightly modified from some teaching material I use with my students. Hope it is useful!

void setup() {
  size(960, 540, P2D);
  background(0);
  smooth(8);
  createBubbles(100);
}


void draw() {
  background(0);
  doBubbles();
}

void mousePressed() {
  checkIfBubbleHit(mouseX, mouseY);
}

//////////////////////////////
// BUBBLE CLASS AND HELPERS //
//////////////////////////////
// An arraylist to hold all the bubbles
ArrayList<Bubble> bubbles = new ArrayList<Bubble>();

// An easy way to make lots of them
void createBubbles(int _thisMany) {
  for (int i = 0; i < _thisMany; i++) {
    bubbles.add(new Bubble());
  }
}
// An easy way to step through, update and display them all
void doBubbles() {
  for (Bubble b : bubbles) {
    b.update();
    b.display();
  }
}
// Check all bubbles to see if they have been clicked on
void checkIfBubbleHit(int _mx, int _my) {
  for (Bubble b : bubbles) {
    b.checkIfHit(_mx, _my);
  }
}

//////////////////
// BUBBLE CLASS //
class Bubble {
  float x, y, diam, diamBurst, floatSpeed;
  float originalDiam;
  int wobbleOffset = int(random(10000));
  int state = 0; // 0 = normal; 1 = clicked;
  Bubble() {
    reset();
  }
  void reset() {
    diam = random(40, 70);
    originalDiam = diam; // we use this for our burst dots mapping
    x = random(diam, width - diam);
    y = height + (diam/2);
    floatSpeed = random(0.5, 2);
    diamBurst = diam + 50;
    state = 0;
  }
  void update() {
    switch(state) {
    case 0: // normal rising bubble
      y = y - floatSpeed;
      if (y <= -diam/2) {
        reset(); // off the top without being clicked? reset to bottom
      }
      break;
    case 1: // bubble has been clicked on
      diam = lerp(diam, diamBurst, 0.1); // burst!
      break;
    }
  }
  void display() {
    switch(state) {
    case 0: // normal rising bubble
      fill(255, 32);
      stroke(255);
      strokeWeight(1);
      // simple wobble using sin, cos and frameCount
      // random wobbleOffset variable means they don't all wobble at the same time
      float xDiam = diam + sin(radians(wobbleOffset + frameCount*5))*5; 
      float yDiam = diam + cos(radians(wobbleOffset + frameCount*5))*5;
      ellipse(x, y, xDiam, yDiam);
      break;
    case 1: // bursting bubble
      // draw the burst dots
      int numBurstDots = 60;
      float angleStep = radians(360.0/numBurstDots);
      strokeWeight(3);
      float burstDotAlpha = map(diam, originalDiam, diamBurst, 255, 0); // makes our burst dots fade out
      stroke(255, burstDotAlpha);
      for (int i = 0; i < numBurstDots; i++) {
        float bx = sin(angleStep*i)*diam/2;
        float by = cos(angleStep*i)*diam/2;
        point(x + bx, y + by);
      }
      if (diam >= diamBurst) { // burst yet?
        reset(); // reset it!
      }
      break;
    }
  }
  void checkIfHit(int _mx, int _my) {
    // check distance from centre of bubble
    if (dist(x, y, _mx, _my) < diam/2) {
      state = 1;
    }
  }
}
2 Likes

Thanks for that!

I worked further on Teljemos version


Bubble b1;

void setup() {
  size(640, 360);
  b1 = new Bubble(50);
}

void draw() {
  background(0);

  // bubble management 
  b1.all();
}

void mousePressed() {
  // bubble explodes 
  if (b1.over()) {
    b1.fall();
  }
}

// ========================================================================================================

class Bubble {

  // constants: these are values that state can have (must be unique (0,1,2...))
  final int RISE    = 0;   // the word final makes them constants 
  final int FALL    = 1; 
  final int RESTART = 2; 
  // variable: current value of the state
  int state=RISE;  // not a state of the program, but the state of the bubble

  float tempSize;
  float w;
  float h;
  boolean move = true;
  float maxW;
  float maxH;
  float minW;
  float minH;
  float wspeed;
  float hspeed;
  float riseY = random(0.8, 3);
  float riseStart;
  float start = random(0, 640);
  float gravity = 0.3;
  float rad;

  Fallout[] fallouts; //might want to use a list (ArrayList)


  Bubble(float tempSize) { // <----------------- Bubble takes value from tab 1 and makes tempSize

    w = tempSize;
    h = tempSize;
    riseStart = tempSize + height;

    rad=tempSize/2;
    maxW = w*1.1+random(0, maxW/2);
    maxH = h*1.1+random(0, maxH/1);
    minW = w/1.1;
    minH = h/1.1;
    wspeed = w/100;
    hspeed = h/75;
  }

  void all() {
    // all (or script()) 

    stroke(255, 255);

    //print("Y ", mouseY);
    //print("  RS ", b1.riseStart);
    //print("  X ", mouseX);
    //println("  Start", b1.start);

    switch(state) {

    case RISE: 
      // rise
      show(); 
      move();
      top(); 
      break; 

    case FALL:
      //fall 
      riseY = riseY - gravity;
      riseStart = riseStart - riseY;
      // new small ball
      ellipse(start, riseStart, 
        3, 3); 
      //shrapnels 
      showExplosion();
      // lower screen border?
      if (riseStart>height+133) {  
        state = RESTART;
      }
      break; 

    case RESTART: 
      // restart 
      state = RISE; 
      riseY = random(0.8, 3); 
      riseStart = tempSize + height+100;
      start = random(w, width-w);
      break;

    default: 
      // What does this state do? When does it run?
      // It just picks up an error, when we forgot a state. 
      // Error 
      println("Error. Unknown state ");
      exit();    
      break; 
      //
    }//switch 
    //
  } // method 

  void show() {
    //noFill();
    //stroke(255);
    noFill();
    stroke(255, 255); 
    strokeWeight(1);
    ellipse (start, riseStart, 
      w, h);

    if (move) {
      w = w + wspeed;

      if ((w > maxW) || (w < minW)) { // Makes bubble wobble.
        wspeed = wspeed * -1;
      }

      if (move) {
        h = h + hspeed;
      }
      if ((h > maxH) || (h < minH)) { // Makes bubble wobble.
        hspeed = hspeed * -1;
      }
    }
  }

  void move() {
    riseStart = riseStart - riseY;
  }

  void top() {
    if (riseStart < tempSize - tempSize -100) { 
      state=RESTART;
    }
  }

  void fall() {
    state=FALL;
    explode();
  }

  boolean over() {
    // returns true or false; true when mouse is on balloon.
    return 
      (mouseX < start + w/2) && 
      (mouseX > start - w/2) &&
      (mouseY < riseStart + h/2) && 
      (mouseY > riseStart - h/2);
  }

  void showExplosion() {
    for (int i = 0; i < fallouts.length; i++) {
      fallouts[i].display();
      fallouts[i].move();
    }//for
  }

  void explode() {

    // init a new explosion 

    // final float RADIUS = rad;
    println(rad);

    //2PI would only shoot down, PI is up down, 
    //halfPi is in 4 dirs and so on
    //you can also just use some random number like 0.3256
    //but try keeping it within 0-2PI

    // full reset 
    fallouts = new Fallout[0];

    // How many shrapnels? Random : 
    final int upperBound = int(random(8, 44)); 

    // angle step accordingly 
    final float ANGLE_INCREMENT = TWO_PI/upperBound;

    // angle 
    float angle=0; 

    for (int i=0; i<=upperBound; i++) {

      // make a new bullet at CURRENT POSITION : start, riseStart
      Fallout newFallout = new Fallout(
        start, riseStart, // start pos 
        cos(angle)*random(.9, 3.8), sin(angle)*random(.9, 3.8) // speed of movement 
        );

      // append it to array
      fallouts = (Fallout[]) append(fallouts, newFallout);
      angle=i*ANGLE_INCREMENT;
    }//for
  }//method
  //
} //class

// ========================================================================================================

class Fallout { 

  // Bullet

  float posX, posY;
  float dirX, dirY;

  int fade = 255; 

  float sizeFallout=random(0.3, 2.8);

  color colorFallout = color(random(255), random(255), random(255)) ;  

  Fallout ( float x_, float y_, 
    float dx_, float dy_) {

    posX = x_;
    posY = y_;
    dirX = dx_;
    dirY = dy_;
  }

  void display() {
    fill(colorFallout, fade); 
    noStroke();
    ellipse(posX, posY, 
      sizeFallout, sizeFallout);
  }

  void move() {
    posX+=dirX;
    posY+=dirY;
    fade-=1; // fade could be at random speed
  }
  //
}//class
//
1 Like

Awesome examples :slight_smile:

@GSA_IxD, Very interesting to see a code written in a different way with the same result!
Thx for that!

@Chrisir, That latest version is really close.
And since you left a bunch of comments I have been able to change it to what we are looking for :slight_smile:
I added the cos and sin plus the rad variable here to start the fallout from around the circle again
Also changed the speed and direction of the explosion. (since its mot of a popping bubble than exploding fireworks) :slight_smile:

      // make a new bullet at CURRENT POSITION : start, riseStart
      Fallout newFallout = new Fallout(
        *cos(angle)*rad+*start, *sin(angle)*rad+*riseStart, // start pos 
        cos(angle)*random(-1.2, 0.8), sin(angle)*random(-1.2, 0.8) // speed of movement 

The upperBound variable should work fine to randomize the number of “particles”. maybe even better than number based on size of main bubble…

Super cool!.. Thx a lot.
Now we are gonna make a array of bubble objects. After that the son said he would like to try rewriting the code for sphere instead of ellipse… :stuck_out_tongue:

Here’s the code with our small edits… Working as we want it to! Thx!

Bubble b1;

void setup() {
  size(640, 360);
  b1 = new Bubble(150);
}

void draw() {
  background(0);

  // bubble management 
  b1.all();
}

void mousePressed() {
  // bubble explodes 
  if (b1.over()) {
    b1.fall();
  }
}

// ========================================================================================================

class Bubble {

  // constants: these are values that state can have (must be unique (0,1,2...))
  final int RISE    = 0;   // the word final makes them constants 
  final int FALL    = 1; 
  final int RESTART = 2; 
  // variable: current value of the state
  int state=RISE;  // not a state of the program, but the state of the bubble

  float tempSize;
  float w;
  float h;
  boolean move = true;
  float maxW;
  float maxH;
  float minW;
  float minH;
  float wspeed;
  float hspeed;
  float riseY = random(1, 3);
  float riseStart;
  float start = random(0, 640);
  float gravity = 0.3;
  float rad;

  Fallout[] fallouts; //might want to use a list (ArrayList)


  Bubble(float tempSize) { // <----------------- Bubble takes value from tab 1 and makes tempSize

    w = tempSize;
    h = tempSize;
    riseStart = tempSize + height;

    rad=tempSize/2;
    maxW = w*1.1+random(0, maxW/2);
    maxH = h*1.1+random(0, maxH/1);
    minW = w/1.1;
    minH = h/1.1;
    wspeed = w/100;
    hspeed = h/75;
  }

  void all() {
    // all (or script()) 

    stroke(255, 100);
    fill(255,200);

    //print("Y ", mouseY);
    //print("  RS ", b1.riseStart);
    //print("  X ", mouseX);
    //println("  Start", b1.start);

    switch(state) {

    case RISE: 
      // rise
      show(); 
      move();
      top(); 
      break; 

    case FALL:
      //fall 
      riseY = riseY - gravity;
      riseStart = riseStart - riseY;
      // new small ball
      
      ellipse(start, riseStart, 
        rad/15, rad/15); 
      //shrapnels 
      showExplosion();
      // lower screen border?
      if (riseStart>height+133) {  
        state = RESTART;
      }
      break; 

    case RESTART: 
      // restart 
      state = RISE; 
      riseY = random(1, 3); 
      riseStart = tempSize + height+100;
      start = random(w, width-w);
      break;

    default: 
      // What does this state do? When does it run?
      // It just picks up an error, when we forgot a state. 
      // Error 
      println("Error. Unknown state ");
      exit();    
      break; 
      //
    }//switch 
    //
  } // method 

  void show() {
    //noFill();
    //stroke(255);
    fill(255,50);
    stroke(255, 255); 
    strokeWeight(1);
    ellipse (start, riseStart, 
      w, h);

    if (move) {
      w = w + wspeed;

      if ((w > maxW) || (w < minW)) { // Makes bubble wobble.
        wspeed = wspeed * -1;
      }

      if (move) {
        h = h + hspeed;
      }
      if ((h > maxH) || (h < minH)) { // Makes bubble wobble.
        hspeed = hspeed * -1;
      }
    }
  }

  void move() {
    riseStart = riseStart - riseY;
  }

  void top() {
    if (riseStart < tempSize - tempSize -100) { 
      state=RESTART;
    }
  }

  void fall() {
    state=FALL;
    explode();
  }

  boolean over() {
    // returns true or false; true when mouse is on balloon.
    return 
      (mouseX < start + w/2) && 
      (mouseX > start - w/2) &&
      (mouseY < riseStart + h/2) && 
      (mouseY > riseStart - h/2);
  }

  void showExplosion() {
    for (int i = 0; i < fallouts.length; i++) {
      fallouts[i].display();
      fallouts[i].move();
    }//for
  }

  void explode() {

    // init a new explosion 

    // final float RADIUS = rad;
    println(rad);

    //2PI would only shoot down, PI is up down, 
    //halfPi is in 4 dirs and so on
    //you can also just use some random number like 0.3256
    //but try keeping it within 0-2PI

    // full reset 
    fallouts = new Fallout[0];

    // How many shrapnels? Random : 
    final int upperBound = int(random(8, 44)); 

    // angle step accordingly 
    final float ANGLE_INCREMENT = TWO_PI/upperBound;

    // angle 
    float angle=0; 

    for (int i=0; i<=upperBound; i++) {

      // make a new bullet at CURRENT POSITION : start, riseStart
      Fallout newFallout = new Fallout(
        cos(angle)*rad+start, sin(angle)*rad+riseStart, // start pos 
        cos(angle)*random(-1.2, 0.8), sin(angle)*random(-1.2, 0.8) // speed of movement 
        );

      // append it to array
      fallouts = (Fallout[]) append(fallouts, newFallout);
      angle=i*ANGLE_INCREMENT;
    }//for
  }//method
  //
} //class

// ========================================================================================================

class Fallout { 

  // Bullet

  float posX, posY;
  float dirX, dirY;

  int fade = 255; 

  float sizeFallout=random(0.3, 2.8);

  color colorFallout = color(random(255,fade), random(255,fade), random(255,fade)) ;  

  Fallout ( float x_, float y_, 
    float dx_, float dy_) {

    posX = x_;
    posY = y_;
    dirX = dx_;
    dirY = dy_;
  }

  void display() {
    fill(colorFallout, fade); 
    noStroke();
    ellipse(posX, posY, 
      sizeFallout, sizeFallout);
  }

  void move() {
    posX+=dirX;
    posY+=dirY;
    fade-=random(10,20); // fade could be at random speed
  }
  //
}//class
//
2 Likes

Nice to hear this!

Thanks also for your Mail!

Chrisir

1 Like

I mentioned the tutorials:

2 Likes

What if the bubbles would explode on clashing with each other??

1 Like

great idea, love to see your code.

I tried it. But it’s not working. I think because of the condition I’ve written for a bubble to pop, each bubble is getting popped by itself. Haven’t been able to rectify it though.

Bubble [] bubbles = new Bubble [100] ;

void setup () {

  size (640, 480);

  for (int indices = 0; indices < bubbles.length; indices += 1) {

    bubbles [indices] = new Bubble (random (width), height, random (80));
  }
}

void draw () {

  background (0);

  for (int indices = 0; indices < bubbles.length; indices += 1) {

    bubbles[indices].ascend ();
    if (bubbles [indices].together (bubbles [indices])) {
      bubbles[indices].pops ();
    }
    bubbles[indices].display ();
    bubbles[indices].top ();
  }
}  

class Bubble {

  float x, y;
  float d;
  float driftX;
  float speedY;
  float alpha;

  Bubble (float tempX, float tempY, float tempD) {

    x = tempX;
    y = tempY;
    d = tempD;
    driftX = 2;
    speedY = random (0.25, 2);
    alpha = random (20, 107);
  }

  void display () {

    fill (255, 223 + (random (-20, 0)), 0, alpha);
    noStroke ();
    ellipse (x, y, d, d);
  }  

  void ascend () {

    x += random (driftX*-1, driftX);

    y -= speedY;
  }    

  void top () {

    if (y+d < 0) {

      y = height;
    }
  }  

  boolean together (Bubble other) {

    float distance = dist((x + d/2), (y+d/2), (other.x+((other.d)/2)), (other.y+((other.d)/2)));

    if (distance == 0) {
      return true;
    } else { 
      return false;
    }
  }  

  void pops () {

    d = 0;
  }
}  

I think in draw you need nested for loop, to check each bubble against every other bubble:

  for (int indices = 0; indices < bubbles.length-1; indices += 1) {
  for (int indices2 = indices ; indices2 < bubbles.length; indices2 += 1) {
       if (bubbles [indices].together (bubbles [indices2])) {
           bubbles[indices].pops ();
           bubbles[indices2].pops ();
       }
}
}
Bubble b1;

void setup() {
  size(640, 360);
  b1 = new Bubble(50);
}

void draw() {
  background(#B3E9F2); //sky blue
  b1.all();
}

void mousePressed() {
  if (b1.over()) {
    b1.fall();
  }
}

//====================================================
//Bubble class tab

class Bubble {

  // constants: these are values that state can have (must be unique (0,1,2...))
  final int RISE    = 0;   // the word final makes them constants 
  final int FALL    = 1; 
  final int RESTART = 2; 
  // variable: current value of the state
  int state=RISE;  // not a state of the program, but the state of the ball

  float tempSize;
  float w;
  float h;
  boolean move = true;
  float maxW;
  float maxH;
  float minW;
  float minH;
  float wspeed;
  float hspeed;
  float riseY = random(0.5, 2);
  float riseStart;
  float start = random(0, 640);
  float gravity = 0.5;

  Bubble(float tempSize) {
    w = tempSize;
    h = tempSize;
    riseStart = tempSize + height;

    maxW = w*1.1+random(0, maxW/2);
    maxH = h*1.1+random(0, maxH/1);
    minW = w/1.1;
    minH = h/1.1;
    wspeed = w/100;
    hspeed = h/75;
  }

  void all() {
    // all (or script()) 

    print("Y ", mouseY);
    print("  RS ", b1.riseStart);
    print("  X ", mouseX);
    println("  Start", b1.start);

    switch(state) {

    case RISE: 
      show(); 
      move();
      top(); 
      break; 

    case FALL: 
      riseY = riseY - gravity;
      riseStart = riseStart - riseY;
      ellipse(start, riseStart, 
        3, 3); // new small ball 
      if (riseStart>height+133) {  // lower screen border
        state = RESTART;
        riseY = 5;
      }
      break; 

    case RESTART: 
      // restart 
      state = RISE; 
      riseStart = tempSize + height+100;
      start = random(w, width-w);
      break;

    default:
      // Error 
      println("Error. Unknown state ");
      exit();    
      break; 
      //
    }//switch 
    //
  } // method 

  void show() {
    //noFill();
    //stroke(255);
    fill(255, 255, 255,130); // RED //new-white transparent
    strokeWeight(1);
    ellipse (start, riseStart, 
      w, h);

    if (move) {
      w = w + wspeed;

      if ((w > maxW) || (w < minW)) { // Makes bubble wobble.
        wspeed = wspeed * -1;
      }

      if (move) {
        h = h + hspeed;
      }
      if ((h > maxH) || (h < minH)) { // Makes bubble wobble.
        hspeed = hspeed * -1;
      }
    }
  }

  void move() {
    riseStart = riseStart - riseY;
  }

  void top() {
    if (riseStart < tempSize - tempSize -100) { 
      state=RESTART;
    }
  }

  void fall() {
    state=FALL;
  }

  boolean over() {
    // returns true or false; true when mouse is on balloon.
    // You had this in mousePressed before. 
    return 
      (mouseX < start + w/2) && 
      (mouseX > start - w/2) &&
      (mouseY < riseStart + h/2) && 
      (mouseY > riseStart - h/2);
  }
  //
}//class
//

In my code, this code above must be

for (int indices2 = indices + 1

obviously

Sorry

But you don’t have a collision anyway

Welcome to the forum!

Great to have you here!