p5.js Particle Workshop 2/3
November 23, 2019
Add a second particle.
This post continues Part 1.
Here’s where we left off at the end of Part 1. The sketch so far draws a single “particle” (white rectangle), that is accelerating downwards.
In this section, we’ll add a second particle.
We’ll separate this into two steps:
Identify the part of the code has responsbility for the single particle.
Copy the identified code, to draw a second particle.
Identifying the code responsible for the particle
The particle is spread across two parts:
The data (represented as variables)
The behavior is represented as code that (1) changes the values of these variables (by assigning to them), and (2) changes the state of the program or of the world (in this case, by calling p5.js functions such as
rect
, that change what’s on the screen).
When we copy the code that involves variables, we’re going to need to create new variables, with new names. In order to set up for this, rename the variables that have to do with the first (and only) particle, so that it’s clear that this is what they do.
This doesn’t crash, and there’s still a moving particle. But it doesn’t completely work — we only see one particle, when the code has two.
The problem is that the two particles are exactly the same. They start out in the same position, and they move the same way. One is drawn on top of the other.
We need to make one of the particles a little different from the other. We’ll do this by giving the second particle a different initial position.
Getting past two
This works for two particles. What would it take to add a third particle? A hundred particles? What makes this difficult?
The problem is that we each particle needs its own lines of code. This makes the program easier to understand, but difficult to extend.
We’ll fix this problem in a series of steps:
Package the data into an object. It’s easier to make copies of an object, than to make lots of copies of the code that defines the data.
Package the behavior into a function. It’s easier to run a function on each object, than to make lots of copies of the code that implements the behavior.
let
defines a variable, with a name and value. A JavaScript object acts like
a set of variables (called “properties”), each with its own name and value. An
object is created by using the { }
syntax, and its property values are
accessed by using the dot notation: the value of anObject.prop
is the value of
the property named prop
, in the object that is the value of anObject
.
This is easier to understand from an example. Study the difference between the data and behavior of particle 1, and the data and behavior of particle 2. These two particles behave in exactly the same way (except that particle 2 appears at a greater x position, since we set its x value differently).
Now update particle 2 to store its data in an object too.
Create a function, that encapsulates the behavior of the first particle.1
This definition of processParticle
only works on particle1
. (That’s the
variable that it mentions in the function body — the lines of code between {
and }
in the function definition.)
We can parameterize processParticle
, by giving it a parameter (here,
particle
). This is the name that gets used inside the function, to refer to
different values. Each time the function is run, this name has whatever value
was used in the function call (inside of draw
). Here, draw
calls
processParticle(particle1)
, so processParticle
is run with particle1
as
the value of particle
.
This is like the use of the word “it” in English.
Since particle 1 and particle 2 have the same behavior (just different initial
data), and processParticle
implements that behavior, we can now use it for
particle2 as well.
The actual code and variable names now describe which values belong to which particle, and which part of the code implements the behavior for each particle. The comments that we created in order to organize the code, are no longer necessary.
Part 3 continues these notes.
- Since p5.js isn’t looking for a function with this name, we don’t need to
export
it like we didsetup
anddraw
. We just need to be consistent between where we define the function (infunction particle()
), and where we use it (inprocessParticle()
inside of thedraw
function).↩