We now have the ability to make crazy numbers of bubbles without writing crazy amounts of code! We're starting to really have some powerful tools at our disposal.
The idea is that every time we click we'll add a bubble to the canvas.
To start, let's just push
some value onto an array every time the mouse is clicked and confirm that the array grows larger.
Here's the starter code-- Notice bubbles
begins as an empty array:
var bubbles = [];
var bubbleSize = 40;
function setup() {
createCanvas(500,400);
}
function draw() {
background(0);
}
function mouseClicked() {
// let's add code here
}
So cool! Every time we click we add an element to the array and we can see that it's happening. Right now that element is just an arbitrary value. Let's go ahead and push on a real bubble object that has the correct properties.
Change the variable bubble
to this:
var bubble = {
x: random(width),
y: random(height)
};
That's closer, but we're still basically at the same place. We have an array with elements in it, but nothing on the canvas.
What if inside of draw
we were constantly looping over the bubbles
array. For every item we should call the ellipse
function to draw the bubble. Let's give it a shot, we need 3 pieces of information for the for
loop.
- Where to start: well array indexes start at
0
so that seems like a good place to me. - When to end: We don't want to keep looping if we're past the end of the array so how about up to
bubbles.length
, whatever that value is currently. - How to count: Definitely wouldn't want to skip over anything in the array so by
1
works fine.
All together that's something like:
for(var i = 0; i < bubbles.length; i++) {
ellipse(___, ___, bubbleSize, bubbleSize);
}
Q: How to fill in those blanks? How do we access the value of a single item from an array?
A: With the index.
Q: Do we have any variables lying around that represent the index we are currently on as we loop through the array?
A: Why of course, i
!
bubbles[i]
, or, in english, the value of the bubbles
array at the index i
, will be the first bubble object the first pass of the loop. Then the second time it will be the second bubble, then the third, then the next all the way until the last bubble. That's just what we want because we want to call ellipse
with the values from each bubble object.
Inside the loop, we'll use whatever the current value of i
is to make a variable called currentBubble
.
Amazing! Every time we click a random bubble appears. It's not moving though. Think for a second about where we need to add to the code to do this.
If you answered inside the loop you are 100% correct. That's the only place where we're able to change, for example, the third bubble's speed or fourth bubble's speed, or any individual bubble's speed-- inside the loop.
The for
loop should look like this:
for(var i = 0; i < bubbles.length; i++) {
var currentBubble = bubbles[i];
ellipse( currentBubble.x, currentBubble.y, bubbleSize, bubbleSize);
currentBubble.x += random(-2, 2);
currentBubble.y += random(-2, 2);
}
We just put together in a few minutes what would have taken us an hour or more before, good work!
I want to make it so clicking anywhere on the canvas creates a new bubble, but clicking on a specific bubble pops it (in p5-world that means removes it from the array).
In the mouseClicked
function we can loop over bubbles
and see if mouseX
and mouseY
are inside of a bubble.
A brief note, when looping over an array and also potentially removing items from it, things can get a little of weird because the array's
length
will be changing. We may not run into problems here, but it's best in these circumstances to loop over the array backwards. It will ensure that no items are missed in the loop. It's fine to just take my word for it for the time being, but feel free to consider the problem more in-depth later. Our loop will look like this:
// the 3 parts of the for loop have not changed:
// where to start, when to stop, how to count.
for(var i = bubbles.length - 1; i >= 0; i--) {
// ...code
}
We should be able to come up with code similar to this:
function mouseClicked() {
for(var i = bubbles.length - 1; i >= 0; i--) {
var currentBubble = bubbles[i];
if (mouseX and mouseY are inside of currentBubble) {
}
}
//...previous code
}
Take a look at the dist()
function. It takes 2 coordinates and returns the distance between them. We want to see if the distance between the center of the circle and (mouseX, mouseY)
is less than the radius of the circle (bubbleSize/2
).
function mouseClicked() {
for(var i = bubbles.length - 1; i >= 0; i--) {
var currentBubble = bubbles[i];
var distance = dist(mouseX, mouseY, currentBubble.x, currentBubble.y)
if (distance <= 20) {
// remove the bubble
}
}
//...previous code
}
The next step is to remove the bubble with splice
. Remember splice
will need the index to remove, for us that's i
, and the number of elements to remove, 1
.
Our removal actually works! It just seems sort of weird that we still add a bubble on every click even if that click was supposed to pop a bubble.
To me, the easiest way to do this is to say, "if any bubbles popped, don't add a bubble". The way we can know in our code if any bubbles popped is to see if the array is any smaller at the end of the function than it was at the beginning.
We'll store the length in a variable called startingLength
and only add a bubble if after the for
loop startingLength == bubbles.length
.
Here's the total final code:
var bubbles = [];
var bubbleSize = 40;
function setup() {
createCanvas(500,400);
}
function draw() {
background(0);
for(var i = 0; i < bubbles.length; i++) {
var currentBubble = bubbles[i];
ellipse( currentBubble.x, currentBubble.y, bubbleSize, bubbleSize);
currentBubble.x += random(-2, 2);
currentBubble.y += random(-2, 2);
}
}
function mouseClicked() {
var startingLength = bubbles.length;
for(var i = bubbles.length - 1; i >= 0; i--) {
var currentBubble = bubbles[i];
var distance = dist(mouseX, mouseY, currentBubble.x, currentBubble.y)
if (distance <= 20) {
bubbles.splice(i, 1);
}
}
if (startingLength == bubbles.length) {
var bubble = {
x: random(width),
y: random(height)
};
bubbles.push(bubble);
}
}
Have fun popping bubbles!
- Instead of each bubble popping immediately on the first click, make the bubble grow larger with each click until it reaches some maximum size. Then, on the next click, it will pop.
You will probably need to add an additional property to each bubble object to track it's size.
- Add a
lifespan
property to each bubble object that has a value of0
when it is pushed into thebubbles
array. Every timedraw
is calledlifespan
should increment by1
. Iflifespan
is greater than300
the bubble should automatically pop.