In these notes you will learn:
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. Then we will see how to create random balls.
We’ll start from the code we developed in Bouncing Around the Screen. Here is the final program:
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;
}
}
Lets convert this program into an OOP style.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 variable attributes:
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!
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.
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.
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;
}
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.