OOP: Bouncing Balls Revisited

In these notes you will learn:

  • How to create and use an object-oriented bouncing ball object.
  • How to create randomly initialized bouncing balls.

Introduction

OOP and animation go well together. In object-oriented animation, the key idea is to represent every animated thing on the screen with its own object. In these notes we’ll re-implement the code for a ball bouncing around the screen in an OOP style.

Going to OOP

We’ll start from the code we developed in Example: A Bouncing Ball:

color orange = color(255, 165, 0);
color white = color(255, 255, 255);

float x, y;

float dx, dy;

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

  // set the ball's initial position and velocity
  x = 250;
  y = 250;
  dx = 1.3;
  dy = -2.77;
}

void draw() {
  // re-draw the background
  background(white);

  // draw the ball
  noStroke();
  fill(orange);
  ellipse(x, y, 50, 50);

  // move the ball to its next position
  x += dx;
  y += dy;

  // hit the top edge?
  if (y < 0) {
    y = 0;
    dy = -dy;
  }

  // hit the bottom edge?
  if (y > 499) {
    y = 499;
    dy = -dy;
  }

  // hit the left edge?
  if (x < 0) {
    x = 0;
    dx = -dx;
  }

  // hit the right edge?
  if (x > 499) {
    x = 499;
    dx = -dx;
  }
}

To convert this program into an OOP style, we need to decide what is the main object we should represent. Since this program is about bouncing balls, it makes sense to create a BouncingBall class to represent them.

When you create a class, you first need to think of a good name, and then list all of its variables:

class BouncingBall {
  float x;   // (x, y) is the center of the ball
  float y;
  float dx;  // (dx, dy) is the velocity of the ball
  float dy;
  float diameter;

  color c;
}

Next we add a constructor to initialize the variables:

class BouncingBall {
    BouncingBall(float init_x, float init_y,
                 float init_dx, float init_dy,
                 float init_diameter,
                 color init_c)
    {
      x = init_x;
      y = init_y;
      dx = init_dx;
      dy = init_dy;
      diameter = init_diameter;
      c = init_c;
    }

    // ..

 } // class BouncingBall

Now we can create a bouncing ball like this:

BouncingBall a;

// ...

a = new BouncingBall(250, 250, -1.25, 1, 50, color(255, 0, 0));

Next we want a bouncing ball to be able to draw itself on the screen, and so we add a render() function:

class BouncingBall {

   // ...

   void render() {
     noStroke();
     fill(c);
     ellipse(x, y, diameter, diameter);
   }

   // ...

} // class BouncingBall

Finally, we need code that updates the position of the ball. Every animated object has such code, and because it is distinct from the rendering code, we put it in its own function:

class BouncingBall {

   // ...

   void update() {
     // move the ball to its next position
     x += dx;
     y += dy;

     // hit the top edge?
     if (y < 0) {
       y = 0;
       dy = -dy;
     }

     // hit the bottom edge?
     if (y > 499) {
       y = 499;
       dy = -dy;
     }

     // hit the left edge?
     if (x < 0) {
       x = 0;
       dx = -dx;
     }

     // hit the right edge?
     if (x > 499) {
       x = 499;
       dx = -dx;
     }
   }

   // ...

} // class BouncingBall

As we’ve seen before, this code updates x and y, and then checks for collisions with the four edges of the screen.

Here is the entire BouncingBall class so far:

class BouncingBall {
  float x, y;    // center of the ball
  float dx, dy;  // velocity of the ball
  float diameter;

  color c;

  BouncingBall(float init_x, float init_y,
               float init_dx, float init_dy,
               float init_diameter,
               color init_c)
  {
    x = init_x;
    y = init_y;
    dx = init_dx;
    dy = init_dy;
    diameter = init_diameter;
    c = init_c;
  }

  void render() {
    noStroke();
    fill(c);
    ellipse(x, y, diameter, diameter);
  }

  void update() {
    // move the ball to its next position
    x += dx;
    y += dy;

    // hit the top edge?
    if (y < 0) {
      y = 0;
      dy = -dy;
    }

    // hit the bottom edge?
    if (y > 499) {
      y = 499;
      dy = -dy;
    }

    // hit the left edge?
    if (x < 0) {
      x = 0;
      dx = -dx;
    }

    // hit the right edge?
    if (x > 499) {
      x = 499;
      dx = -dx;
    }
  }
} // class BouncingBall

With the BouncingBall class completed, we can now write the main code:

BouncingBall a;

void setup() {
  size(500, 500);
  smooth();
  a = new BouncingBall(250, 250, -1.25, 1, 50, color(255, 0, 0));
}

void draw() {
  background(255);
  a.render();
  a.update();
}

This code is short and relatively easy to understand. Indeed, the most complicated part is the call to the constructor: make sure to pass the parameters in the right order!

Three Bouncing Balls

Lets write a demo program that makes three balls bounce around the screen. This turns out to be quite easy since all the hard work is taken care of by BouncingBall:

BouncingBall a;
BouncingBall b;
BouncingBall c;

void setup() {
  size(500, 500);
  smooth();
  a = new BouncingBall(250, 250, -1.25, 1, 50, color(255, 0, 0));
  b = new BouncingBall(350, 250, -1.04, -1.11, 50, color(0, 255, 0));
  c = new BouncingBall(350, 250, 1.04, -0.11, 50, color(0, 0, 255));
}

void draw() {
  background(255);
  a.render();
  a.update();

  b.render();
  b.update();

  c.render();
  c.update();
}

As you can see, adding extra balls is not difficult. Most of our effort goes into the constructors, i.e. deciding the location, velocity, and color of the ball.

Random Bouncing Balls

A fun way to make BouncingBalls is to choose there size, position, velocity, and color at random.

To get random numbers in Processing we’ll use the random(lo, hi) function. The expression random(lo, hi) returns a (uniformly) randomly chosen value in the range from lo to hi:

a <= random(a, b) < b

Note that this inequality is asymmetric: random(a, b) might sometimes return a, but it will never return b.

Now we can use this to choose values within random ranges that make sense for each of the variables we want:

  • For the ball’s x and y position, lets insist that they always start near the center of the screen. Thus x and y should be chosen in, say, the range 200 to 300:

    float rx = random(200, 300);
    float ry = random(200, 300);
    
  • For the ball’s velocity, represented by dx and dy, lets choose values in the range -2.0 to 2.0:

    float rdx = random(-2.0, 2.0);
    float rdy = random(-2.0, 2.0);
    
  • For the diameter of the ball, we want to make sure that it’s not too big or too small, and so lets select it from the range 25 to 75:

    float rdiam = random(25, 75);
    
  • Finally, to choose a random color we need to choose random red, green, and blue values in the range 0 to 255:

    float rred = random(0, 255);
    float rgreen = random(0, 255);
    float rblue = random(0, 255);
    

Now lets combine all of this into a single function:

BouncingBall makeRandomBall() {
  float rx = random(200, 300);
  float ry = random(200, 300);
  float rdx = random(-2.0, 2.0);
  float rdy = random(-2.0, 2.0);
  float rdiam = random(25, 75);
  float rred = random(0, 255);
  float rgreen = random(0, 255);
  float rblue = random(0, 255);

  BouncingBall ball = new BouncingBall(rx, ry, rdx, rdy, rdiam,
                                       color(rred, rgreen, rblue));
  return ball;
}

Now it is easy to create random balls:

BouncingBall a;
BouncingBall b;
BouncingBall c;

void setup() {
  size(500, 500);
  smooth();
  a = makeRandomBall();
  b = makeRandomBall();
  c = makeRandomBall();
}

void draw() {
  background(255);
  a.render();
  a.update();

  b.render();
  b.update();

  c.render();
  c.update();
}

This program is identical to the previous one, except we initialize a, b, and c in setup using randomBouncingBall.

Choosing Random Colors

Creating random colors is something you might want to do in many different sample programs, so it is useful to create a special function just for doing that:

color randomColor() {
  float r = random(0, 255);
  float g = random(0, 255);
  float b = random(0, 255);
  color c = color(r, g, b);
  return c;
}

Note

Another way to write the randomColor function is like this:

color randomColor() {
  return color(random(0, 255), random(0, 255), random(0, 255));
}

This does the same thing as the version of the function in the notes, but this version is shorter and more compact. Many professional programmers would prefer this version because it is shorter and, once you get used to it, quite readable. However, many beginning programmers find this version too compact and hard to understand. In this course, we’ll try to stick to the longer, simpler version.

Now we can simplify makeRandomBall a little:

BouncingBall makeRandomBall() {
  float rx = random(200, 300);
  float ry = random(200, 300);
  float rdx = random(-2.0, 2.0);
  float rdy = random(-2.0, 2.0);
  float rdiam = random(25, 75);

  BouncingBall ball = new BouncingBall(rx, ry, rdx, rdy, rdiam, randomColor());

  return ball;
}

100 Bouncing Balls?

Three bouncing balls are nice, but how could we make 100 balls bounce around the screen? If you think about this for a moment, you will realize that if we have 100 balls, we’ll need 100 variables, e.g.:

BouncingBall ball1;
BouncingBall ball2;
BouncingBall ball3;
// ...
BouncingBall ball100;

void setup() {
   size(500, 500);
   smooth();
   ball1 = randomBouncingBall();
   ball2 = randomBouncingBall();
   ball3 = randomBouncingBall();
   // ...
   ball100 = randomBouncingBall();
}

void draw() {
  background(255);

  ball1.render();
  ball1.update();

  ball2.render();
  ball2.update();

  ball3.render();
  ball3.update();

  // ...

  ball100.render();
  ball100.update();
}

This program will work, but it is far too much typing. Plus, if we wanted, say, 200 or 300 balls, then even more typing is needed.

This is clearly impractical. In the next set of notes we’ll see a solution to this problem.

Questions

  1. What’s the smallest integer value that random(1, 10) can return? The biggest?

Programming Questions

  1. Modify the randomBouncingBall function so that the alpha value of the color is also set randomly. Test it in the bouncing balls program from the notes.
  2. Modify the BouncingBall class so that no part of the ball ever goes off the screen. Hint: You only need to modify the update() function.