12. Multiple objects
Arrays
Let us consider the original random wanderer sketch, from when we first looked at state, putting on hold for now the more recent object-oriented version. We used two variables, x and y, to keep track of where the wanderer was, and then each frame we drew an ellipse at the current (x,y) and updated them by random amounts. The key parts of the code are as follows:
var x=200, y=200; function setup() { createCanvas(400,400); } function draw() { background(0); ellipse(x,y,20,20); x += random(-10,10); y += random(-10,10); }
Now suppose we wanted to have two wanderers. We could define variables x2 and y2, and duplicate and slightly modify the code to update and use them the same way.
var x=200, y=200; var x2=200, y2=200; function setup() { createCanvas(400,400); } function draw() { ellipse(x,y,20,20); ellipse(x2,y2,20,20); x += random(-10,10); y += random(-10,10); x2 += random(-10,10); y2 += random(-10,10); }
ooking at the previous code it is obvious that there is a major issue. Every single time we have to add more wanderers, we have to copy and mpdify the code by hand. Things become pretty wild if we try say 10 wanderers.
Fortunately, the Array programming construct (details in the Programming notes section) makes it much easier to deal with the states of multiple things. Rather than declaring a single x variable and a single y variable, we declare (and create, via new) two of each. We then access the x values using the array access ([]) operator as x[0] and x[1] (instead of x and x2) and similarly with the y values.
var x = new Array(2); var y = new Array(2); function setup() { createCanvas(400,400); x[0] = 200; y[0] = 200; x[1] = 200; y[1] = 200; } function draw() { background(0); ellipse(x[0],y[0],20,20); ellipse(x[1],y[1],20,20); x[0] += random(-10,10); y[0] += random(-10,10); x[1] += random(-10,10); y[1] += random(-10,10); }
The benefit of this is that we "packed" all similar values together into arrays of things. But the rest of the code did not really get better. Looking carefully at the code though, you should note that we basically do the same thing on all elements of the arrays. This is where loops help. By setting up a loop, iterating over which wanderer we're dealing with, we only have to write the code once. Even better, we can have any number of wanderers.
// How many wanderers var num=5; // Their positions var x = new Array(num); var y = new Array(num); function setup() { createCanvas(400,400); smooth(); noStroke(); background(0); // loop to initialize all the coordinates of the wanderers for (var i=0; i<x.length; i++) { // Initialize the i-th wanderer x[i] = width/2; y[i] = height/2; } } function draw() { fill(0,3); noStroke(); rect(0,0,width,height); fill(255); stroke(128); // loop to draw and update each wanderer for (var i=0; i<x.length; i++) { // Draw the i-th wanderer ellipse(x[i],y[i],20,20); // Update the i-th wanderer x[i] += random(-10,10); y[i] += random(-10,10); } }
Note that we made the loop range up to (but not including) x.length. The length field tells us how many elements there are in the array (here 5). Five elements are numbered 0, 1, 2, 3, and 4, so we want the loop variable to start at 0 and go through 4. If you try to access past the end of the array (using more elements than you asked for), Processing will give you an "array index out of bounds" exception.
One other nice example of arrays is the Processing example Basics | Input | StoringInput, which Shiffman also covers in example 9-8. The idea here is that the very last element has the current mouse position, the one before that the previous position, and so forth. Thus for each frame, we move the position for index i to index i-1. But before we do that, we better move i-1 to i-2, etc. The loop handles that. Note that the loop starts from index 1, moving its information to 0; the info for 0 just disappears. The size of the ellipse depends on the index -- larger indices (more recent positions) have larger ellipses.
Arrays of objects
Since x[i] and y[i] go together to establish the position of the i-th wanderer, it makes sense to keep a single array of Wanderer objects, rather than two separate arrays of their coordinates. Here's a rewriting of the above sketch to use the Wanderer class from the earlier lecture.
var num=5; var wanderers = new Array(num); function setup() { createCanvas(400,400); smooth(); noStroke(); background(0); for (var i=0; i<wanderers.length; i++) { // Initialize the i-th wanderer wanderers[i] = new Wanderer(random(width),random(height), 10,color(random(200,255),random(200,255),random(200,255))); } } function draw() { fill(0,3); rect(0,0,width,height); for (var i=0; i<wanderers.length; i++) { wanderers[i].draw(); // Draw the i-th wanderer wanderers[i].update(); // Update the i-th wanderer } }
// A class describing what a Wonderer is, including what he can do class Wanderer { // Initialize a Wanderer to be at the given coordinates constructor(x, y, r, c) { this.x = x; this.y = y; this.r = r; this.c = c; } // Draw a Wanderer, wherever it happens to be now draw(){ fill(this.c); ellipse(this.x,this.y,this.r*2,this.r*2); } // Update the state of a Wanderer update(){ var r = this.r this.x+=random(-r,r); this.y+=random(-r,r); } }