(Extra) Creating an Explosion with a Particle System

In these notes you will learn:

  • How to design an “explosion” consisting of multiple particles.

Introduction

You’ve no doubt seen lots of different kinds of explosions in video games, and so in this note we will try creating our own explosion. There’s no standard kind of explosion, so we will make things up as we go.

Shards: The Pieces of an Explosion

What is an explosion? Roughly speaking, it is a collection of particles that begin at the same point, and then quickly move outwards.

Taking an OOP approach, this suggests that we ought to write a class to represent the individual particles of the explosion. Lets make them circles, so in addition to the position and velocity we will store a radius and radius change rate:

class Shard {
  float x, y;    // position
  float dx, dy;  // velocity
  float r;       // radius
  float dr;      // rate of change of radius

  boolean visible;

  Shard() {
     reset();
  }

  void reset() {
    dx = random(-2.0, 2.0);   // try changing these
    dy = random(-2.0, 2.0);   // initial random
    r = random(4.0, 10.0);    // values to get different
    dr = random(-0.5, 0.0);   // kinds of explosions
    visible = true;
  }

  void render() {
     // ...
  }

  void update() {
     // ...
  }
} // class Shard

The variable visible controls whether or not the shard is to be drawn on the screen: we will only draw the shard if visible is true.

The constructor calls the reset function, which randomly assigns the variables of the shard. We could have put the code in reset directly into the constructor, but by putting it in its own function it is much easier to re-use particles for later explosions.

Now lets write the render() and update() functions. The render() function is relatively simple:

void render() {
   if (visible) {
      ellipse(x, y, 2 * r, 2 * r);
   }
}

Note

We could have written the if-statement in render() like this:

if (visible == true) {
   ellipse(x, y, 2 * r, 2 * r);
}

This is equivalent to writing if (visible). You can use whatever version you prefer, but keep in mind that most experienced programmers prefer if (visible) because it is shorter and still relatively readable.

The update() function needs a little bit more code. The shards move outwards from the center of the explosion and get smaller as they move. Once the radius of a shard is less than 1, it becomes invisible:

void update() {
   x += dx;
   y += dy;
   r += dr;

   if (r < 1) {    // make the shard disappear when it is very small
      visible = false;
   }
}

Here is the final Shard class:

class Shard {
  float x, y;    // position
  float dx, dy;  // velocity
  float r;       // radius
  float dr;      // rate of change of radius

  boolean visible;

  Shard() {
     reset();
  }

  void reset() {
    dx = random(-2.0, 2.0);   // try changing these
    dy = random(-2.0, 2.0);   // initial random
    r = random(4.0, 10.0);    // values to get different
    dr = random(-0.5, 0.0);   // kinds of explosions
    visible = true;
  }

  void render() {
    if (visible) {
      ellipse(x, y, 2 * r, 2 * r);
    }
  }

  void update() {
    x += dx;
    y += dy;
    r += dr;

    if (r < 1) {   // when the shard gets too small, make it disappear
      visible = false;
    }
  }
} // class Shard

Testing a Single Explosion

Now that we have written the Shard class, lets write a demo that make an explosion wherever the user clicks:

void setup() {
   size(500, 500);
   smooth();
   noStroke();
   fill(255, 255, 0);   // yellow

   // ...
}

void draw() {
   // draw an explosion, or nothing if the explosion is not occurring ...
}

void mousePressed() {
   // cause an explosion centered at the mouse pointer
}

For simplicity, lets permit only one explosion on the screen at a time. Thus we only need to store one ArrayList of Shard objects:

final int NUM_SHARDS = 100;  // number of particles in an explosion
ArrayList<Shard> shards;

This creates a variable of type ArrayList<Shard>. It does not create the ArrayList, nor the Shard objects within it. We do that in setup():

final int NUM_SHARDS = 100;  // number of particles in an explosion
ArrayList<Shard> shards;     // initialized to null

void setup() {
  size(500, 500);
  smooth();
  noStroke();
  fill(255, 255, 0);   // yellow

  // an explosion consists of many small shards
  shards = new ArrayList<Shard>();
  int i = 0;
  while (i < NUM_SHARDS {
    Shard s = new Shard();
    shards.add(s);
    ++i;
  }
}

Note we follow a standard pattern for using an ArrayList of objects:

  1. Declare the ArrayList variable:

    ArrayList<Shard> shards;   // initially null
    

    This just creates an ArrayList variable: it doesn’t create the ArrayList itself, and it doesn’t create the objects inside it. By default, ArrayList variables are given the initial value of null.

  2. Create the ArrayList:

    shards = new ArrayList<Shard>();
    

    The expression new ArrayList<Shard>() creates an empty ArrayList with nothing in it yet.

  3. Add the objects:

    while (i < NUM_SHARDS {
      Shard s = new Shard();
      shards.add(s);
      ++i;
    }
    

    Every time the body of the loop is executed, a new Shard is created and added to the right end of shards.

The code for draw() calls the render and update function for each object:

void draw() {
  background(0);
  for(Shard s : shards) {
    s.render();
    s.update();
  }
}

Lastly, lets write the code for mousePressed. We decided above that an explosion occurs when and where the user clicks:

void mousePressed() {
  for(Shard s : shards) {
    s.reset();
    s.x = mouseX;
    s.y = mouseY;
  }
}

Recall that mousePressed() is automatically called every time the mouse is clicked.

Notice that this code re-uses the particles from the explosion instead of creating new ones. This is to save memory. If you use a lot of explosions in a program than the particles could take up a significant chunk of memory that could eventually slow your program down.

The Program So Far

class Shard {
  float x, y;    // position
  float dx, dy;  // velocity
  float r;       // radius
  float dr;      // rate of change of radius

  boolean visible;

  Shard() {
     reset();
  }

  void reset() {
    dx = random(-2.0, 2.0);   // try changing these
    dy = random(-2.0, 2.0);   // initial random
    r = random(4.0, 10.0);    // values to get different
    dr = random(-0.5, 0.0);   // kinds of explosions
    visible = true;
  }

  void render() {
    if (visible) {
      ellipse(x, y, 2 * r, 2 * r);
    }
  }

  void update() {
    x += dx;
    y += dy;
    r += dr;

    if (r < 1) {   // when the shard gets too small, make it disappear
      visible = false;
    }
  }
} // class Shard

//////////////////////////////////////////////////////////////////////

final int NUM_SHARDS = 100;  // number of particles in an explosion
ArrayList<Shard> shards;  // initialized to null

void setup() {
  size(500, 500);
  smooth();
  noStroke();
  fill(255, 255, 0);   // yellow

  // an explosion consists of many small shards
  shards = new ArrayList<Shard>();
  for(int i = 0; i < NUM_SHARDS; ++i) {
    Shard s = new Shard();
    shards.add(s);
  }
}

void draw() {
  background(0);
  for(Shard s : shards) {
    s.render();
    s.update();
  }
}

void mousePressed() {
  for(Shard s : shards) {
    s.reset();
    s.x = mouseX;
    s.y = mouseY;
  }
}

An Explosion Class

Our demo program draws a single explosion, which is useful for testing. However, if you want multiple explosions, or if you want to use them in another program, then its best to create a class to handle the explosion.

So lets create a class called Explosion:

class Explosion {
  ArrayList<Shard> shards;

  Explosion(int numParticles, float x, float y) {
    shards = new ArrayList<Shard>();
    int i = 0;
    while (i < numParticles) {
      Shard s = new Shard();
      s.x = x;
      s.y = y;
      shards.add(s);
      ++i;
    }
  }

  void render() {
    for(Shard s : shards) {
      s.render();
    }
  }

  void update() {
    for(Shard s : shards) {
      s.update();
    }
  }

} // class Explosion

This contains all the code from our explosion demo program in the previous section, but re-organized. Now the demo program can be easily re-written:

Explosion e;

void setup() {
  size(500, 500);
  smooth();
  noStroke();
  fill(255, 255, 0);   // yellow
}

void draw() {
  background(0);
  if (e == null) return;
  e.render();
  e.update();
}

void mousePressed() {
  e = new Explosion(50, mouseX, mouseY);
}

We’ve introduced a new idea in draw():

if (e == null) return;

This if-statement immediately exits the function if variable e does not refer to an object.

Recall that when you create an object variable like e, it’s initialized with the special value null. When e is null, the statements e.render() and e.update() are both errors because null is not an object and so doesn’t have any functions named render() or update(). Without this if-statement, the program would crash with a run-time error when it tries to execute e.render().

Adding Gravity

Lets add gravity to our explosions so that as the particles fly away from the center they also get pulled downwards by a gravitational force.

We’ll do this by adding gravity to each shard:

class Shard {
  float x, y;    // position
  float dx, dy;  // velocity
  float r;       // radius
  float dr;      // rate of change of radius
  float gravity; // force of gravity

  boolean visible;

  Shard() {
    dx = random(-2.0, 2.0);   // try changing these
    dy = random(-2.0, 2.0);   // initial random
    r = random(4.0, 10.0);    // values to get different
    dr = random(-0.5, 0.0);   // kinds of explosions
    gravity = random(0.1, 0.2);
    visible = true;
  }

  void render() {
    if (visible) {
      ellipse(x, y, 2 * r, 2 * r);
    }
  }

  void update() {
    x += dx;
    y += dy;
    r += dr;

    dy += gravity;

    if (r < 1) {   // when the shard gets too small, make it disappear
      visible = false;
    }
  }
} // class Shard

What we’ve done here is add a new variable called gravity that we set to random value in the constructor. Then in update we add gravity to dy.

The result is quite nice: the particles now get pulled downwards as they fly out.

Programming Questions

  1. Modify the Shard class so that shards are drawn with a color. Set the color randomly in the constructor. Test your changes with one of the sample programs.
  2. Create a demo program that allows 2 (or more, if you wish) explosions to appear on the screen at the same time.
  3. Make a demo program that initially displays a balloon slowly floating up the screen. When the mouse pointers clicks on the balloon, it pops (disappears) and explodes. Use an Explosion object to make the explosion.
  4. Create a fireworks demo.

The Program

class Shard {
  float x, y;    // position
  float dx, dy;  // velocity
  float r;       // radius
  float dr;      // rate of change of radius
  float gravity; // force of gravity

  boolean visible;

  Shard() {
     reset();
  }

  void reset() {
    dx = random(-2.0, 2.0);   // try changing these
    dy = random(-2.0, 2.0);   // initial random
    r = random(4.0, 10.0);    // values to get different
    dr = random(-0.5, 0.0);   // kinds of explosions
    gravity = random(0.1, 0.2);
    visible = true;
  }

  void render() {
    if (visible) {
      ellipse(x, y, 2 * r, 2 * r);
    }
  }

  void update() {
    x += dx;
    y += dy;
    r += dr;

    dy += gravity;

    if (r < 1) {   // when the shard gets too small, make it disappear
      visible = false;
    }
  }
} // class Shard

/////////////////////////////////////////////////////////////////////////

class Explosion {
  ArrayList<Shard> shards;

  Explosion(int numParticles, float x, float y) {
    shards = new ArrayList<Shard>();
    for(int i = 0; i < numParticles; ++i) {
      Shard s = new Shard();
      s.x = x;
      s.y = y;
      shards.add(s);
    }
  }

  void render() {
    for(Shard s : shards) {
      s.render();
    }
  }

  void update() {
    for(Shard s : shards) {
      s.update();
    }
  }

} // class Explosion

/////////////////////////////////////////////////////////////////////////

Explosion e;

void setup() {
  size(500, 500);
  smooth();
  noStroke();
  fill(255, 255, 0);   // yellow
}

void draw() {
  background(0);
  if (e == null) return;
  e.render();
  e.update();
}

void mousePressed() {
  e = new Explosion(50, mouseX, mouseY);
}