1. Object Oriented Programming

In this exploration, we examine the idea of an object, and begin writing our first object-oriented programming study.

1.1 Old Ball Code

This code is from the discussion on Bouncing Balls. We need to keep track of the ball's velocities in the x and y direction, keep track of where it is (x, y), and the global time.

In [1]:
float g = 9.8; 

float vx;
float vy;

int x;
int y;

float dt;  
float t;

void setup() {
    size(200, 500);
    x = width/2;
    y = 50;
    dt = 0.1;  
    t = 0;
    vx = 50.0;
    vy = 0.0;
}

void drawBall(int x, int y, int w, int h) {
    fill(255, 0, 0);
    ellipse(x, y, w, h);
}

void draw() {
    // gravity
    vy = vy + g * dt;
    
    dx = vx * dt;    
    if (((x + dx) > width) || ((x + dx) < 0)) {
        vx = vx * -0.8;
    } else {
        x = x + dx;
    }
    
    dy = vy * dt;
    if (((y + dy) > height) || ((y + dy) < 0)) {
        vy = vy * -0.8;
    } else {
        y = y + dy;
    }

    drawBall(x, y, 10, 10);
    
    t = t + dt;
}
Sketch #1:

Sketch #1 state: Loading...

What if we wanted to add more balls? We would need to add variables for vx, vy, x, y... for each ball!

1.2 Bouncing Ball as an Object

A better way is to let each ball keep track of its own properties. We do this by making a class definition, and storing each set of variables inside the Ball object.

Here we will move all of the ball-specific variables "into the Ball class" and leave the others. We create an array of Balls, and bounce each one.

In [33]:
// Globals:

float g = 9.8;   // gravity
float dt = 0.1;  // change in time
float t = 0;     // current time

Ball [] balls; // the balls

class Ball {
    int x;
    int y;
    int w;
    int h;
    float vx;
    float vy;

    // Jargon: the "constructor"
    Ball(int x, int y, int w, int h, float vx, float vy) {
        // Idea: "this" refers to the current ball
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.vx = vx;
        this.vy = vy;
    }
    
    // Jargon: an object's function is called a "method"
    void moveYourself() {
        this.vy = this.vy + g * dt;

        dx = this.vx * dt;    
        if (((this.x + dx) > width) || ((this.x + dx) < 0)) {
            this.vx = this.vx * -0.8;
        } else {
            this.x = this.x + dx;
        }

        dy = this.vy * dt;
        if (((this.y + dy) > height) || ((this.y + dy) < 0)) {
            this.vy = this.vy * -0.8;
        } else {
            this.y = this.y + dy;
        }
    }
    
    void drawYourself() {
        fill(255, 0, 0);
        ellipse(this.x, this.y, this.w, this.h);
    }
}

void setup() {
    size(200, 500);
    balls = new Ball[1];
    // Jargon: we create an "instance" of the Ball class:
    balls[0] = new Ball(width/2, 50, 10, 10, 50.0, 0.0);
    t = 0;
}

void draw() {
    balls[0].moveYourself();
    balls[0].drawYourself();
    
    t = t + dt;
}
Sketch #33:

Sketch #33 state: Loading...

1.3 Variations

Cut and paste the above code and try some variations:

  1. More than one ball
  2. Add color to the balls
  3. Add a ball where you click the mouse
In [41]:
// Globals:

float g = 9.8;   // gravity
float dt = 0.1;  // change in time
float t = 0;     // current time

Ball [] balls; // the balls

class Ball {
    int x;
    int y;
    int w;
    int h;
    float vx;
    float vy;
    color c;

    // Jargon: the "constructor"
    Ball(int x, int y, int w, int h, float vx, float vy) {
        // Idea: "this" refers to the current ball
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.vx = vx;
        this.vy = vy;
        this.c = color(random(255), random(255), random(255));
    }
    
    // Jargon: an object's function is called a "method"
    void moveYourself() {
        this.vy = this.vy + g * dt;

        dx = this.vx * dt;    
        if (((this.x + dx) > width) || ((this.x + dx) < 0)) {
            this.vx = this.vx * -0.8;
        } else {
            this.x = this.x + dx;
        }

        dy = this.vy * dt;
        if (((this.y + dy) > height) || ((this.y + dy) < 0)) {
            this.vy = this.vy * -0.8;
        } else {
            this.y = this.y + dy;
        }
    }
    
    void drawYourself() {
        fill(this.c);
        ellipse(this.x, this.y, this.w, this.h);
    }
}

void setup() {
    size(200, 500);
    balls = new Ball[1000];
    // Jargon: we create an "instance" of the Ball class:
    for (int i = 0; i < balls.length; i++) {
        balls[i] = new Ball(width * random(), height * random(), 10, 10, random(50) - 25, 0.0);
    }
    t = 0;
}

void draw() {
    background();
    for (int i = 0; i < balls.length; i++) {
        balls[i].moveYourself();
        balls[i].drawYourself();
    }
    t = t + dt;
}
Sketch #41:

Sketch #41 state: Loading...

1.4 Collision Detection

It would be cool if the balls could detect when they run into each other, just like it detects when it hits a wall. To do that, we go through the other balls, and identify when they "overlap".

Then, we simply swap their velocities.

In [42]:
// Globals:

float g = 9.8;  // gravity
float dt = 0.1;  // change in time
float t = 0;     // current time

Ball [] balls; // the balls

float distance(float x1, float y1, float x2, float y2) {
    return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)))
}

class Ball {
    color c;
    int x;
    int y;
    int w;
    int h;
    float vx;
    float vy;

    Ball(color c, int x, int y, int w, int h, int vx, int vy) {
        this.c = c;
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.vx = vx;
        this.vy = vy;
    }
    
    void move() {
        this.vy = this.vy + g * dt;      
        for (int i = 0; i < balls.length; i++) {
            if (balls[i] != this) {
                if (distance(this.x, this.y, balls[i].x, balls[i].y) < (this.w/2 + balls[i].w/2)) {
                    temp = this.vx;
                    this.vx = balls[i].vx;
                    balls[i].vx = temp;
                    temp = this.vy;
                    this.vy = balls[i].vy;
                    balls[i].vy = temp;
                    break;
                }
            }
        }
        dx = this.vx * dt;    
        dy = this.vy * dt;
        if (((this.x + dx) > width) || ((this.x + dx) < 0)) {
            this.vx = this.vx * -0.8;
        } else {
            this.x = this.x + dx;
        }
        if (((this.y + dy) > height) || ((this.y + dy) < 0)) {
            this.vy = this.vy * -0.8;
        } else {
            this.y = this.y + dy;
        }
    }
    
    void draw() {
        fill(this.c);
        ellipse(this.x, this.y, this.w, this.h);
    }
}

void setup() {
    size(200, 500);
    balls = new Ball[3];
    balls[0] = new Ball(color(255, 0, 0), width/2, 40, 10, 10, 50.0, 0.0);
    balls[1] = new Ball(color(0, 255, 0), width/2, 50, 10, 10, -40.0, 0.0);
    balls[2] = new Ball(color(0, 0, 255), width/2, 60, 10, 10, 0.0, 0.0);
    t = 0;
}

void draw() {
    balls[0].move();
    balls[0].draw();
    balls[1].move();
    balls[1].draw();
    balls[2].move();
    balls[2].draw();
    
    t = t + dt;
}
Sketch #42:

Sketch #42 state: Loading...

Variations:

  1. Create different kinds of balls (e.g., different shapes... just use your previous drawObject here)
  2. Make the Physics more realistic (e.g., add some loss of energy, randomness)
  3. Create them where you click the mouse.
  4. Take into account the balls' masses (e.g., like this
  5. Have some things in the environment that don't move (gravity should not effect them, and they have no velocities)
  6. Have some locations that inject velocities (e.g., shoot objects into the air)
In [48]:
// Globals:

float g = 9.8;  // gravity
float dt = 0.1;  // change in time
float t = 0;     // current time

Ball [] balls; // the balls

float distance(float x1, float y1, float x2, float y2) {
    return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)))
}

class Ball {
    color c;
    int x;
    int y;
    int w;
    int h;
    float vx;
    float vy;

    Ball(color c, int x, int y, int w, int h, int vx, int vy) {
        this.c = c;
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.vx = vx;
        this.vy = vy;
    }
    
    void move() {
        this.vy = this.vy + g * dt;      
        for (int i = 0; i < balls.length; i++) {
            if (balls[i] != this) {
                if (distance(this.x, this.y, balls[i].x, balls[i].y) < (this.w/2 + balls[i].w/2)) {
                    temp = this.vx;
                    this.vx = balls[i].vx;
                    balls[i].vx = temp;
                    temp = this.vy;
                    this.vy = balls[i].vy;
                    balls[i].vy = temp;
                    break;
                }
            }
        }
        dx = this.vx * dt;    
        dy = this.vy * dt;
        if (((this.x + dx) > width) || ((this.x + dx) < 0)) {
            this.vx = this.vx * -0.8;
        } else {
            this.x = this.x + dx;
        }
        if (((this.y + dy) > height) || ((this.y + dy) < 0)) {
            this.vy = this.vy * -0.8;
        } else {
            this.y = this.y + dy;
        }
    }
    
    void draw() {
        fill(this.c);
        ellipse(this.x, this.y, this.w, this.h);
    }
}

void setup() {
    background();
    size(200, 500);
    balls = new Ball[100];
    for (int i = 0; i < balls.length; i++ ) {
        balls[i] = new Ball(color(random(255), random(255), random(255)), 
                            width * random(), height * random(), 10, 10, 
                            random(50.0) - 25, 0.0);
    }
    t = 0;
}

void draw() {
    for (int i = 0; i < balls.length; i++ ) {
        balls[i].move();
        balls[i].draw();
    }    
    t = t + dt;
}
Sketch #48:

Sketch #48 state: Loading...
In [52]:
// Globals:

float g = 9.8;  // gravity
float dt = 0.1;  // change in time
float t = 0;     // current time

Ball [] balls; // the balls

float distance(float x1, float y1, float x2, float y2) {
    return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)))
}

class Ball {
    color c;
    int x;
    int y;
    int w;
    int h;
    float vx;
    float vy;

    Ball(color c, int x, int y, int w, int h, int vx, int vy) {
        this.c = c;
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.vx = vx;
        this.vy = vy;
    }
    
    void move() {
        this.vy = this.vy + g * dt;      
        for (int i = 0; i < COUNT; i++) {
            if (balls[i] != this) {
                if (distance(this.x, this.y, balls[i].x, balls[i].y) < (this.w/2 + balls[i].w/2)) {
                    temp = this.vx;
                    this.vx = balls[i].vx;
                    balls[i].vx = temp;
                    temp = this.vy;
                    this.vy = balls[i].vy;
                    balls[i].vy = temp;
                    break;
                }
            }
        }
        dx = this.vx * dt;    
        dy = this.vy * dt;
        if (((this.x + dx) > width) || ((this.x + dx) < 0)) {
            this.vx = this.vx * -0.8;
        } else {
            this.x = this.x + dx;
        }
        if (((this.y + dy) > height) || ((this.y + dy) < 0)) {
            this.vy = this.vy * -0.8;
        } else {
            this.y = this.y + dy;
        }
    }
    
    void draw() {
        fill(this.c);
        ellipse(this.x, this.y, this.w, this.h);
    }
}

void setup() {
    size(200, 500);
    balls = new Ball[100];
    COUNT = 0;
    t = 0;
}

COUNT = 0;

void mousePressed() {
    balls[COUNT] = new Ball(color(random(255), random(255), random(255)), 
                            mouseX, mouseY, 10, 10, random(50) - 25, 0);
    COUNT++;
}

void draw() {
    for (int i = 0; i < COUNT; i++) {
        balls[i].move();
        balls[i].draw();
    }    
    t = t + dt;
}
Sketch #52:

Sketch #52 state: Loading...