Simple motion - Simple OOP

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.

The simplest case

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:

  1. defining custom classes.
  2. creating objects that are instances of these classes.
  3. using these objects.

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".

Adding variables

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.

Moving alone

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:

  1. Before 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().
  2. Inside 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.
  3. Each frame, inside 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).
  4. In turn, the 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...

Moving ensemble

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).

Moving en masse

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:

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.

Putting theory into practice

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)?

What's next

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