p5.js Particle Workshop 3/3
December 03, 2019
Add multiple particles. Make them bounce from each other, and from the mouse.
This post continues Part 2.
Here’s where we left off in the previous post.
Draw circles insead of squares.
Put the two particles in an array.
The draw
functions read the particles from the array, instead of from the
variables particle1
and particle2
.
The two calls to processParticle
can be replaced by a call to
forEach(processParticle)
. This applies processParticle
to each of the items
in the array.
Now draw
will work with an array of any length.
Give the particles a random initial position.
Math.random()
returns a floating point number (basically, a real number or
decimal, although it is not exactly the same as either of these) between 0.0
(inclusive) and 1.0 (exclusive).
Multiplying such as number by windowWidth
yields a number between 0.0 and
windowWidth
.
The code that creates the initial value for particle1
is now identical to the
code that creates the initial value for particle2
.
Extract the code that computes an initial value for particle1
into a new
function makeParticle
, and use this function to create the initial value for
particle1
.
Use this same function to create the initial value for particle2
.
We’re only using the particle1
and particle2
variables to store the return
value from makeParticle
long enough to put it in the particles
array. Put
these values directly into the array, instead. This allows us to remove the
particle1
and particle2
variables.
(We’re gradually getting rid of the code that’s specific to their being exactly two particles.)
Here’s a trick for making an array of length 2, filled by calling makeParticle
twice.
Array(2)
makes an array with two elements. (They both have the special value
undefined.
.) map
calls its argument makeParticle
once for each element in
the array, and makes a new array that contains the return values from all these
calls to makeParticle
. That new array becomes the value of particles
.
The advantage of using this trick to create the array is that we can as easily make 100 particles instead of just two.
If we create the array inside of setup
, then we can use the p5.js random
function. random(windowWidth)
returns a floating-point number between 0.0 and
windowWidth
. It’s simpler than the code we were using before.
The p5.js functions are only available inside the setup()
and draw()
functions (and the event handler functions such as keyPressed()
and
mouseClicked()
), and the functions that they call. This is why we had to move
the initialization of particles
inside of setup
, in order to use p5.js’s
random
.
Give the particles a small initial random x and y velocity. They’ll each start out moving at a randomm speed (between 0 and 1.41 pixels per second), in a random direction.
The p5.js function random(a, b)
, with two arguments, returns a value between a
and b. It’s equivalent to the JavaScript expression a + (b - a) * Math.random()
.
Make the particles bounce them when they hit the “floor”.
Bounce them off the walls too. (But not — yet — the ceiling.)
I prefer separate functions for moving and drawing the particles.
The particles bounce when they get close to each other.
This is implemented by looping over each pair of particles p1
and p2
,
testing whether their centers are close enough, and, if they are, applying a
force that moves them away from each other.
The nested forEach
’s loop over pairs of particles. The outer forEach
loops
over each particle, the inner forEach
then loops over each particle, and the
if
statement keeps the code beneath it from comparing a particle to itself.
This code actually examines each pair twice: At one point it sets p1
to
particles[0]
and p2
to particles[1]
, and later p1 == particles[1]
and
p2 == particles[0]
.
This is mostly
harmless.
It means that the forces are applied twice, and it means that the
bounceParticles
takes twice as long as it needs to. We’ll address this at the
end of this article.
Turn off gravity, so we can see the particle interactions more easily.
With gravity off, the particles should bounce off the ceiling as well.
Add viscosity (the particles slow down over time).
Bounce the particles off the mouse position as well as each other. This lets you move the mouse around the screen to scatter particles from the mouse path.
We handle this with minimal modifications considering the mouse to be just another object, and running the bounce code over all the objects including the mouse, instead of just the particles.
This updatees the mouse “object“‘s dx
and dy
properties, which we then
ignore.
Advanced Stuff
As promised, here’s the change to only consider each pair of particles once (to
process objects[0]
and objects[1]
, but not also to separately process
objects[1]
and objects[0]
).
map
and forEach
call the supplied function with two arguments, the array
element and its index within the array. Use this to only consider pairs where
the p1 precedes p2.
Wouldn’t it be cool if there a function like forEach
, that called our function
on each pair of elements from the array, taking care not to call our function
with both (array[0], array[1])
and (array[1], array[0])
?
There can be! Now that we’ve written the logic that handles this, we can extract
it tto a new function forEachPair
. Now it’s easier to read what
bounceParticles
is doing.