by Ariel Malka
OOP stands for Object-Oriented Programming, a central feature present in Java, Processing's core language. But what is the relation between motion and OOP?
In short, this relation is not obvious when controlling the motion of a single element on screen, but it becomes clearer when we want to handle an arbitrary number of elements that share a common behavior: example given.
For additional material related to this tutorial, please refer to the following thread in the Processing forums. If you have some unanswered questions or wish to send some feedback, this thread is also the right place to express yourself. Finally, if you find a typo or have some personal comments, please contact the author.
Consider the 2 following pieces of code. The one on the left simply draws a shape on screen using a custom function, while the one on the right does exactly the same thing, but using OOP. (The highlighted blocks denote similarities between the two examples.)
// DRAW 1 void setup() { drawShape(); // calling a function
}void drawShape() // a custom function
{ rect(20, 50, 10, 10); } |
// DRAW 1, OBJECT ORIENTED void setup() { Drawer myDrawer = new Drawer(); // creating an object of type Drawer // assigned to the variable myDrawer myDrawer.drawShape(); // calling a function (method)
}// of the newly created object class Drawer // a custom class named Drawer { void drawShape() // a custom function (method) of the Drawer class
}
{ rect(20, 50, 10, 10); } |
The Object-Oriented paradigm is based on:
In the OOP example above, we defined a class that we choose to name Drawer
. In order to define a class, we simply create a block of the form:
class Something { // things going on inside, all related to this class... }
Instead of Something
, we can put any name we find relevant, providing it doesn't start by a number. By convention, class names start by a capital letter. Note that it's not important where the class-definition-block is placed in the code. In the example above, the Drawer
class is defined at the end of the code, but it could have been placed before...
Inside the Drawer
class-definition-block, we defined a custom function named drawShape
that is specific to this class (we say that it's a member of this class). Note that instead of using the term "function", we use the term method, when applied to OOP. By convention, method names start by a lower case letter.
Once we defined a class, we need _ at least _ to create an object that will make use of it. This is what the following line does:
Drawer myDrawer = new Drawer()
In Java, the new
statement is responsible for dynamically creating an object of a certain type. In occurrence, we create a new object of type Drawer
which is assigned to the variable myDrawer
.
Finally, once we created an object, we need to do something with it. With our current Drawer
class, there is not much to do, except using the drawShape
method. This is what the following line does:
myDrawer.drawShape()
Which in result draws a shape on screen. Note that the dot between myDrawer
and drawShape
reflects what we call the dot-syntax.
In this first example, the use of OOP is not bringing any added-value, except revealing the structure of OOP programming. Let's see another useless but insightful example, before "getting real".
Once again, the leftmost code is using "standard" programming, while the rightmost is using OOP. The 2 pieces of code are doing the same thing: drawing a shape (using a custom function) whose position is controlled by 2 variables.
// DRAW 2 // declaring global variables
float x; float y; void setup() { // initializing variables
x = 50; y = 70; drawShape();
}void drawShape()
{ rect(x, y, 10, 10); } |
// DRAW 2, OBJECT ORIENTED void setup() { Drawer myDrawer = new Drawer(); // initializing variables (fields)
myDrawer.x = 50; myDrawer.y = 70; myDrawer.drawShape();
}class Drawer { // global variables (fields) of the Drawer class
float x; float y; void drawShape()
}
{ rect(x, y, 10, 10); } |
Let's analyze the OOP code: what changed compared to the previous example?
First, the Drawer
class-definition-block is now including:
float x; float y;
It declares 2 variables that are specific to the Drawer
class. Just like the drawShape
method, these 2 variables (we use the term fields when applied to OOP) are members of the class.
So each time a new object of type Drawer
is created, 2 fields (x and y) of type "float" are declared, and associated exclusively with this object.
The other part that has been added is the following:
myDrawer.x = 50; myDrawer.y = 70;
Executed just after a new object is created and assigned to the myDrawer
variable, these 2 lines are setting the values of x and y for the newly created object. Here again, the dot between myDrawer
and the field name reflects the previously mentioned dot-syntax.
To summarize, we've seen that a class is made of an arbitrary number of members. Members can be fields (variables) or methods (functions). In order to access the members of a class, we use the dot-syntax.
This time, we start using the basic "setup / loop" structure of Processing. The following pieces of code show a ball moving on screen. The example on the left is using "standard" coding, while the right one is using OOP.
// MOTION 1 // declaring global variables
float x; float y; void setup() { size(200, 200); framerate(25); // initializing variables
}x = 30; y = 80; void loop() { background(127); drawShape(); // calling a function for drawing the ball
}x = x + 2; y = y + 1; void drawShape()
{ ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); } |
// MOTION 1A, OBJECT ORIENTED Ball myBall; // declaring a global variable to hold an object of type Ball void setup() { size(200, 200); framerate(25); myBall = new Ball(); // creating an object of type Ball // initializing fields
}myBall.x = 30; myBall.y = 80; void loop() { background(127); myBall.run(); // invoking the object's method named run } class Ball // defining a custom class { // defining fields
float x; float y; void run() { drawShape(); // invoking a method for drawing the ball
}x = x + 2; y = y + 1; void drawShape()
}
{ ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); } |
How the OOP example works:
First of all, we defined a class named Ball
whose goal is to make a shape move on screen. For this purpose, the class holds two fields (x and y) that will contain position information. In addition, the class has two methods: run
for animating things and drawShape
for drawing things.
Then, in chronological order:
setup()
, a global variable named myBall
of type Ball
is declared. Note that this variable must be global since it's going to be used inside setup()
and inside loop()
.setup()
, a new object of type Ball
is created and assigned to the variable myBall
. Then, the fields of the newly created object are initialized with some values.loop()
: the run
method is invoked on our object. Note that the use of "run" as a method name is purely cosmetic (any other name could have been used instead).run
method is drawing the ball on screen and is updating its position.One of the interesting features in this example is that it reduces the amount of code within loop()
, while making the intentions of the programmer more obvious.
Finally, this example reveals the common structure used in Processing when animating things using Object Oriented Programming. Whenever you feel ready to experiment with OOP in Processing, you can just copy this example and start changing things...
A clear advantage of Object Oriented Programming is that once a class has been defined, it's very easy to use more than one object of that type.
Following, on the left is the previous OOP example. On the right, we add another moving ball. (The highlighted blocks now denote the parts that are different between the two examples.)
// MOTION 1A, OBJECT ORIENTED, 1 BALL Ball myBall;
void setup() { size(200, 200); framerate(25); myBall = new Ball();
myBall.x = 30;
}myBall.y = 80; void loop() { background(127); myBall.run();
}class Ball { float x; float y; void run() { drawShape(); x = x + 2; y = y + 1; } void drawShape() { ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); } } |
// MOTION 1B, OBJECT ORIENTED, 2 BALLS Ball myBall_1;
Ball myBall_2; void setup() { size(200, 200); framerate(25); myBall_1 = new Ball();
myBall_2 = new Ball(); myBall_1.x = 30;
}myBall_1.y = 80; myBall_2.x = 120; myBall_2.y = 50; void loop() { background(127); myBall_1.run();
}myBall_2.run(); class Ball { float x; float y; void run() { drawShape(); x = x + 2; y = y + 1; } void drawShape() { ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); } } |
As you can see, the difference between the 2 examples is minor, and most importantly the Ball
class didn't changed. It was just a matter of "declaring / creating / initializing / running" an additional object.
We created two objects that share the same behavior, but with different initial values. As we already mentioned, every time a new object is created, it receives its own set of fields (in occurrence: x and y).
It's now time to experience "industrial motion", where an arbitrary number of objects are moving at the same time.
Following, on the left is our previous OOP example with only one moving ball. On the right, 33 randomly positioned balls are moving together.
// MOTION 1A, OBJECT ORIENTED, 1 BALL Ball myBall;
void setup() { size(200, 200); framerate(25); myBall = new Ball();
myBall.x = 30;
}myBall.y = 80; void loop() { background(127); myBall.run();
}class Ball { float x; float y; void run() { drawShape(); x = x + 2; y = y + 1; } void drawShape() { ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); } } |
// MOTION 1C, OBJECT ORIENTED, N BALLS
Ball[] myBalls; // declaring a global variable
// to hold an array of Ball objects void setup() { size(200, 200); framerate(25); myBalls = new Ball[33]; // creating an (empty) array with a
// capacity of 33 elements of type Ball for (int i = 0; i < 33; i++) // iterating 33 times
}{ myBalls[i] = new Ball(); // populating the array
// with new Ball objects myBalls[i].x = random(0, 200);
}myBalls[i].y = random(0, 200); void loop() { background(127); for (int i = 0; i < 33; i++) // iterating 33 times
}{ myBalls[i].run();
}class Ball { float x; float y; void run() { drawShape(); x = x + 2; y = y + 1; } void drawShape() { ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); } } |
Here again, the Ball
class didn't changed. The new elements that have been introduced are:
for
loops.random
function to generate 33 different position values.One of the features of Object Oriented Programming is that whenever a class exists, it's possible to create an array of objects of that type. So for instance, the following:
Ball[] myBalls = new Ball[33]
is creating an array of 33 objects of type Ball
, assigned to the myBalls
variable.
Note that since the myBalls
variable needs to be global (it's going to be used both in setup()
and in loop()
), we need to use a split form:
Ball[] myBalls;
before setup()
myBalls = new Ball[33];
within setup()
So, once the myBalls
array have been created, it's possible to access its elements using the form:
myBalls[0]
gives access to the first item in the array.
myBalls[32]
gives access to the last item in the array.
Now pay attention to the following form:
myBalls[i]
gives access to the item at index i
in the array, providing i
is a variable of type int
with a value between 0 and 32.
It is the principle used twice in the actual example to iterate through the array, using for
loops.
The first for
loop is filling the array with 33 new Ball
objects (it's important to understand that when creating a new array, it is empty!). Within the same loop, each newly created object is given some initial position values, generated randomly (note that any other technique for generating different positions could have been used instead of the random
function).
The second for
loop is simply calling the run
method on each of the objects in the array.
It should be clear now that one advantage of working with OOP in Processing is the ability to model a specific behavior and reproduce it at a greater scale.
If you feel like doing exercise, the following piece of code (a simple bouncing ball) is waiting to be translated to OOP!
// BOUNCE 1 // declaring global variables float x; float y; float vx; float vy; void setup() { size(200, 200); framerate(25); // initializing variables x = 30; y = 80; vx = 2.5; // the horizontal component of the velocity vy = 4.25; // the vertical component of the velocity } void loop() { background(127); drawShape(); // updating the position of the ball using velocity x = x + vx; y = y + vy; // the 2 following tests are keeping the ball within the boundaries of the screen if (x < 0 || x > width) { vx = -vx; // inverting the horizontal velocity component of the ball whenever it reaches the left or the right of the screen } if (y < 0 || y > height) { vy = -vy; // inverting the vertical velocity component of the ball whenever it reaches the bottom or the top of the screen } } void drawShape() { ellipseMode(CENTER_RADIUS); noStroke(); fill(255); ellipse(x, y, 4, 4); }
Can you make an Object Oriented version where 50 balls are bouncing, given random starting positions (within the boundaries of the screen) and random velocity components (between -5 and 5)?
In order to keep things simple and effective, this tutorial deliberately ignored 2 central concepts in Object Oriented Programming: Constructors and Inheritance. In addition, it is possible to implement some very useful things with OOP, like generating objects on the fly and controlling their lifespan...
All this could be the subject of another tutorial, meanwhile, quoting a famous programmer: waste your spare-time coding!
version: 1.00
last updated: October 23, 2003