In these notes you will learn:
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.
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
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 that standard pattern for using arrays of objects:
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.
Create the array:
shards = new ArrayList<Shard>();
The expression new ArrayList<Shard>() creates an empty ArrayList with nothing in it yet.
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.
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;
}
}
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().
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.
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);
}