PROCESSING
ADVANCED PROCESSING
Lets look at where we left of in the bounds checking and flow control tutorial:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int x;
int y;
int r; // radius
int direction_x;
int direction_y;
int speed;

void setup(){
    size(500,500); 
    smooth();
    x = int(random(r,(width-r)));
    y = int(random(r,(height-r)));
    r = 15;
    speed = 5;
    direction_x = 1;
    direction_y = 1;
    ellipseMode(CENTER);

    fill(random(0,255),random(0,255),random(0,255));
}

void draw(){
    background(100,100,100);
    ellipse(x,y,r*2,r*2);

    x+=(speed * direction_x);
    y+=(speed * direction_y);

    if(x+r > width){ 
        direction_x = -1;
    }else if(x-r < 0){
        direction_x = 1;
    }

    if(y+r > height){
       direction_y = -1;
    }else if(y-r < 0){
       direction_y = 1; 
    }
}
This example works fine for one ball, but introducing more balls within this structure requires new variables for each ball which would start to become unwieldy.

In this tutorial we'll look at structuring are code so that we can easily introduce multiples of an object.
To start out we need to look at a way to hold multiple items, and loop over them to be able to draw each one individually.

To do this we can use a specific element, called an array. Most every programing language uses arrays since they are a great way to store multiple items. We can use arrays just like other variables we were using, such as an int, except arrays are more like containers and can hold multiple values instead of just one.

An example of an array could be: Items in arrays are accessed via their index in the array, with array indexes starting at 0. In the above pseudo code array the element at index 0 would be joe, with 4 items in total ranging from index 0-3.

In processing, we declare arrays in a similar way that we declare other variables:
01
02
03
String[] names;
// ... then later in our setup
names = new String[5];
Like creating an int variable we first set up a name and a type at the top of our sketch. Although you'll notice we need to add one more detail to our declaration -- and that is the two square brackets [ ] that signify that we are going to use the named item as an array.

In our setup, instead of assigning a value, we request a new array object of a certain type and with how many slots our array will need.

So the above code will create an array that has 5 slots (indexes 0-4) which are strings. We could use it like so:
01
02
03
04
05
06
07
String[] names;
// ... then later in our setup
names = new String[5];
// ... later in our code to set a value at index 0
names[0] = "joe";
// ... and to retrieve index 0
pintln(names[0]); // prints "joe" to our console
And to express the pseudo code above as a valid processing array:
01
02
03
04
05
06
07
String[] names;
// ... then later in our setup
names = new String[5];
names[0] = "joe";
names[1] = "sarah";
names[2] = "jim";
names[3] = "cassandra";
This isn't very exciting but its with this structure that we can then look at loops to go over each item.
The second programming concept we need to learn to be able to develop a multi-object sketch is loops. Using loops we can iterate or move over each element in our array and access it - for instance to draw it or to increment its x position to animate it. There are a couple different flavors of loops but the most useful one for our needs is the for loop:
01
02
03
04
for(int i=0; i < 4; i++){
    println(i);
}
// this will print 0 1 2 3 in our console
To break that down, what a for loop does is first create the variable that you'll use inside of the loop to either iterate over and array or in the above case print to the console a number. this is the: int i=0; part. This step of the loop runs only once, at the beginning, and establishes the variable for the loop.

The next part of the loop is a conditional that checks whether the variable (in our case i) is less than 4. This is where you can specify how long or for how many iterations the loop should go. (in our case 4 iterations from 0-3)

The last step is simply to increment the variable to move the loop along, for instance if the loop has already run 4 times (we've received 0 1 2 3 in the console) then the next iteration will check to see if i (which is now 4) is less than 4 (which it is not) to determine if it'll run again and increment again.

If the condition is false (i > 4) then the loop stops.

So with that we can now iterate or loop over our array and print out each element in it:
01
02
03
04
05
06
// names.length is a convenient way to specify the length
// of an array instead of having to hard code the value at 5
for(int i=0; i < names.length; i++){
    println(names[i]);
}
// this will print joe sarah jim cassandra in our console
Now with arrays and loops we can look at one last piece needed to be able to implement our sketch.
The last thing we need to do is set up our ball as a class which is basically packaging all the variables and functions inside an object so that each ball has and maintains its own state.

The power of this is that each ball can be given random or otherwise determined values at the start and act in the world of our sketch based on those values and be as independent or interactive with each other was we want to program. By creating each ball as an instance of a class we can also effectively clone it as much as we want, storing it in an array and looping over each one to move its state forward (like adding to its x and y position, etc.)

An easy way to look at a class is basically a microcosm of our sketch. It will have its own variables and the equivalent of a setup function along with a draw function.

Lets look at the basic structure:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
class Ball{
    int x;
    int y;
    
    // equivalent to setup
    Ball(){
      x = 50;
      y = 50;
    }
    
    void draw(){
        ellipse(x,y,50,50);
    }
}
Like the example at the top - we create two variables for our x and y position. But instead of putting them at the top of our sketch we can declare them inside of our class so that these specific variables we'll be considered class variables only used by each instance of the ball.

The Ball() function is what is called a Constructor which is the equivalent to our setup() function in our sketch. It is called once we instantiate or create a new ball from our Ball class. Just like in our setup, we can assign our variables values or do other initial things. Also like our sketch, we've created a draw() function inside our Ball class which takes care of actually drawing our ball to the window via the ellipse method.

Now that we have created a basic Ball class lets look at how we create a ball from it:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Ball{
    int x;
    int y;

    // equivalent to setup    
    Ball(){
      x = 50;
      y = 50;
    }

    void draw(){
        ellipse(x,y,50,50);
    }
}

// create a new variable with the type Ball
Ball myNewBall;

void setup(){
    size(500,500); 
    // assign our variable the value of a new ball
    // this creates a new unique instance of our ball class 
    myNewBall = new Ball();
}

void draw(){
    // use the Ball class draw() method
    myNewBall.draw();
}
At the top of our sketch we now have our newly written Ball class.

To create a ball from it, we first need to create a variable that can hold one, therefore we create a new variable myNewBall and give it the type Ball.

In our setup we can now create and assign a new ball to the variable we created above. The new Ball() creates a unique instance of our class and assigns it to our variable via the myNewBall =

In draw function of our sketch we can then use our new ball's draw function to draw it to our window.
Now that we have our basic structure we can start to move over some of the code we had written previously for or original ball bouncing sketch. Instead of putting the various bits in the draw function of our sketch I'll add it to the draw of our Ball class so that each instance of the ball we make will animate according to it.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Ball{
    int x;
    int y;
    int r; // radius
    int direction_x;
    int direction_y;
    int speed;
    int ballRed, ballGreen, ballBlue;

    Ball(){
        x = int(random(r,(width-r)));
        y = int(random(r,(height-r)));
        r = 15;
        speed = 5;
        direction_x = 1;
        direction_y = 1;
        ballRed = int(random(0,255));
        ballGreen = int(random(0,255));
        ballBlue = int(random(0,255));
    }

    void draw(){
        x+=(speed * direction_x);
        y+=(speed * direction_y);

        if(x+r > width){ 
            direction_x = -1;
        }
        else if(x-r < 0){
            direction_x = 1;
        }

        if(y+r > height){
            direction_y = -1;
        }
        else if(y-r < 0){
            direction_y = 1; 
        }

        fill(ballRed,ballBlue,ballGreen);
        ellipse(x,y,50,50);
    }
}


Ball myNewBall;

void setup(){
  size(500,500);
  smooth(); 
  ellipseMode(CENTER);
  myNewBall = new Ball();
}

void draw(){
  background(80,80,80);
  myNewBall.draw();
}
If everything has transferred right we should have the animation at the left as it was before.

Now for the final step, we can add an array so that we can create and animate multiple balls, and loop over each ball in the array via a for loop.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Ball{
    int x;
    int y;
    int r; // radius
    int direction_x;
    int direction_y;
    int speed;
    int ballRed, ballGreen, ballBlue;

    Ball(){
        x = int(random(r,(width-r)));
        y = int(random(r,(height-r)));
        r = 15;
        speed = 5;
        direction_x = 1;
        direction_y = 1;
        ballRed = int(random(0,255));
        ballGreen = int(random(0,255));
        ballBlue = int(random(0,255));
    }

    void draw(){
        x+=(speed * direction_x);
        y+=(speed * direction_y);

        if(x+r > width){ 
            direction_x = -1;
        }
        else if(x-r < 0){
            direction_x = 1;
        }

        if(y+r > height){
            direction_y = -1;
        }
        else if(y-r < 0){
            direction_y = 1; 
        }

        fill(ballRed,ballBlue,ballGreen);
        ellipse(x,y,50,50);
    }
}

// set up a variable that will be where we hold the balls
Ball[] ballContainer;

void setup(){
  size(500,500);
  smooth(); 
  ellipseMode(CENTER);
  // create a container with 300 balls in it
  ballContainer = new Ball[300];
  for(int i=0; i < ballContainer.length; i++){
     ballContainer[i] = new Ball();   
  }
}

void draw(){
  background(80,80,80);
  for(int i=0; i < ballContainer.length; i++){
     ballContainer[i].draw();   
  }
}
Now we've created an array of balls, looped over each slot in the array and assigned a newly created ball to it. Finally, in the draw of our sketch we iterate again over each element of the array and draw the ball.
At the moment each ball is the same size and speed so they move in unison. If we randomize the size and speed we can get develop a richer ecosystem of objects:
01
02
03
04
05
06
07
08
09
10
11
Ball(){
  x = int(random(r,(width-r)));
  y = int(random(r,(height-r)));
  r = int(random(1,30));
  speed = int(random(1,5));
  direction_x = round(random(0,1))==0? -1:1;
  direction_y = round(random(0,1))==0? -1:1;
  ballRed = int(random(0,255));
  ballGreen = int(random(0,255));
  ballBlue = int(random(0,255));
}
I also set noStroke() in the sketch setup() so that each ellipse doesn't have a black outline.
As a final example here is a sketch that uses a class to calculate a few physical properties of each ball like drag and gravity. With the these techniques you can build deep and complex ecosystems with a relatively small amount of code. (click to reanimate)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
float min_mass = 0.75;
float max_mass = 5.0;
float gravity = 1.0; // assume y only
float drag = -0.03; 
float velocity = 40;

class Ball{
  float mass; 
  float x,y;
  float velx,vely; 
  float accelx,accely;
  float bounce=1.0;
  float diameter;
  int r,g,b;

  Ball(){
    x=random(0,width); 
    y=random(0,height); 
    mass=random(min_mass,max_mass);
    velx = random(-velocity,velocity); // initial velocity
    vely = random(-velocity,velocity);
    diameter = mass * 10;
    r = int(random(0,255));
    g = int(random(0,255));
    b = int(random(0,255));
  }

  void update(){
    // calculate acceleration
    float gravity_force = gravity / mass; 

    float accelx = (drag * velx) / mass;
    float accely = gravity_force + (drag * vely) / mass;

    // add acceleration to velocity 
    velx += accelx;
    vely += accely;

    // update location
    x += velx;
    y += vely;

    // reset acceleration 
    accelx=0.0;
    accely=0.0;     

    // check borders
    if((y + diameter/2) > height) {
      vely *= -bounce;
      y = height - diameter/2;
    }
    if((x + diameter/2) > width) {
      velx *= -bounce;
      x = width - diameter/2;
    }else if((x - diameter/2) < 0){
      velx *= -bounce;
      x = 0 + diameter/2;      
    }
  }

  void draw(){
    ellipseMode(CENTER);
    //stroke(0);
    noStroke();
    fill(r,g,b);
    ellipse(x,y,mass*10,mass*10);
  }
}

Ball[] ballCollection = new Ball[100];

void setup(){
  size(500,500);  

  smooth();
  for(int i=0; i < ballCollection.length; i++){
    ballCollection[i] = new Ball();  
  }
}

void draw(){
  for(int i=0; i < ballCollection.length; i++){
    ballCollection[i].update();
    ballCollection[i].draw();
  }
}

void mouseClicked() {
    for(int i=0; i < ballCollection.length; i++){
    ballCollection[i].velx = random(-velocity,velocity);
    ballCollection[i].vely = random(-velocity,velocity);
  }
}