Your browser does not support the canvas tag.

Week 8
Hackers

OOPs I did it again ... Classes!


A few weeks ago we were using multiple lists to manage a bunch of different circles moving around the screen at once. We had a arrays for the x and y locations as well as the speed of both x and y. This worked reasonably well, but managing these lists can get out of hand. There's another way to conceptualize the problem. What if if each moving circle had it's own motivation, it's own individual script. Think of Grand Central station at rush hour. People are moving through from all angles and speeds, yet there are very few collisions. If there was one person, a sort of "person traffic controller" that managed everyone their task would be very difficult, ultimately they could do it, but the results would be much slower. Instead each person has their own motivation (the gate they are going to) and a shared set of rules (try your best not to run into other people). The results are way faster.

In programming the catchphrase for this approach to programming is called OOP (object oriented programming)

Truth is we've already used classes indirectly. In some of the external libraries, like video, we create an "instance" of the class before the setup. The line "Capture cam;" is creating an object called cam that holds its own set of variables.

In this first example we declare a class and initialize it (or, to be technically accurate we "create an instance of the class"). Then in the draw we reference the class's display function to draw it on the screen. Of course there are simpler ways to draw a circle, but this is just the first step.

Class terminology:

        /* create a vairable myBall that will be an instance of the class BallClass
        this vaiable is empty right now. It does the same things as saying "PImage img"
        only with a class we create ourselves! Hooray! */ 
        BallClass myBall; 

        void setup() {
          size(400, 400); 
          // make an instance of the class. This actually makes the ball and runs
          // the "constructor" code which is empty right now)
          myBall = new BallClass();
      }

      void draw() {
        background(1);
        // run the method that is "in" the object called myBall
        myBall.display();
    }

    class BallClass {
      //varialbes that are global within every method in the class
      float x = 300; 
      float y = 200;
      // the constructor (empty for now but still necessary) 
      BallClass() {
      } 
      //method
      void display() { 
        ellipse(x, y, 50, 50);
      }
  }

Passing values to the Object

Obviously if this was all there was to it we wouldn't bother - there are faster ways to draw a circle. The power comes from being able to make multiple objects that behave the same but have different variables. One way to do this is by passing values to the object when you create it - you do this by sending the variable to the constructor. You can send different types of variables, but you have to make sure you have the constructor set up to properly recieve the incoming values.
This sketch builds off the last by sending values to the class that the object uses to set the x and y. Notice how we take the incoming values and assign them to the class' global variables. This means we can use them throughout all the functions in the class.

  BallClass myBall; 

  void setup() {
    size(400, 400); 
    myBall = new BallClass(300, 200);
}

void draw() {
  background(1);
  myBall.display();
  myBall.moveball(); 
}

class BallClass {
  float x, y; 
  float speedX = 2.8; 
  float speedY = -.7; 
  BallClass(float _x, float _y) {
    x = _x; 
    y = _y;
  } 
  void moveball() {
    //check wall collision
    if (x > width || x < 0) {
      speedX = speedX * -1;
    }
    if (y > height || y < 0) {
      speedY = speedY * -1;
    }
    // iterate the ball variable
    x += speedX; 
    y += speedY;
  }
  void display() { 
    ellipse(x, y, 50, 50);
  }
}
Okay. Now make 3 balls that move around the screen that have different colors. Make the balls various sizes and speeds
Think of a system to check the collision between balls, (even if you have to use psudo-code).




Great, but now we're getting back into the problem we had before. If we want lot of objects they all have their own variables and routines, but we still have to create and run the script(s) on each one. What if we had an array of objects? So each time we create a new instance of a class it goes into a list - then we can use for loops to access them from the list! Sweet.

Yes, you can make a regular array of objects, and in past years I've taught it that way first, but here we start to get into a problem with the way regular arrays work - they're fixed size. What if I have a game of blowing bubbles. It makes sense to make the bubble an object, but how big should the array be? My program shouldn't crash just because the user wants to make 1000 bubbles. Wouldn't it be good to have an array that can grow or shrink basked on the number of bubbles you need? ArrayList to the rescue!


arrayLists
An arrayList is a special kind of list designed to hold objects. Why do we need to bother? Two reasons. They come with some new keywords

    ArrayList <BallClass> ballslist = new ArrayList<BallClass>();
    void setup() {
      size(600, 400);
      smooth();
  }
  void newball() {
    for (int i = 0; i < 20; i ++) {
      float speedx = (random(-4, 4)); 
      float speedy = (random(-4, 4)); 
      // create 20 new objects and put them in the list
      ballslist.add(new BallClass(mouseX, mouseY, speedx, speedy));
    }
}

void draw() {
  background(255);
  smooth(); 
  for (int i = ballslist.size()-1; i >= 0; i--) { 
    // gets the current ball and names this instance "dball".
    BallClass dball = ballslist.get(i); 
    dball.moveballs();
    dball.display();
    // this is looking for a return of true or false
    if (dball.finished() == true) { 
      ballslist.remove(i);
    }
  }
}

void mouseReleased() {
  newball();
}

class BallClass {
  float x, y, speedx, Balpha, speedy; // declare the variables used in the class
  color ballColor = color(100, 120, 160, 255); 
  int BallWidth = 20; 
  float fade = 256; 
  BallClass(float tempX, float tempY, float tempspeedx, float tempspeedy) { 
    x = tempX; // assign incoming temporary values to the variables you set above. 
    y = tempY;
    speedx = tempspeedx; 
    speedy = tempspeedy;
  }

  boolean finished() {
    fade -= 2; 
    if (fade <= 0) {
      return true;
    }
    else {
      return false;
    }
  }
  void moveballs() {
    x += speedx; 
    y += speedy;
  }
  void display() {
    noStroke(); 
    ballColor = color(red(ballColor), green(ballColor), blue(ballColor), fade); 
    fill(ballColor); 
    ellipse(x, y, BallWidth, BallWidth);
  }
}

note, sometimes instead of using a traditional for loop to go through all the objects in the array you will see syntax like this.
  for (BallClass dball : ballslist) {
    dball.display();
    dball.moveballs();
  }
This will work great for calling methods, but if you need to change anything on the object it's not so good because you don't have access to which number you're currently addressing.

return

In the code above we used a return. This is pretty useful.
So far all our functions begin with "void" because we are not sending a value back to another function. But maybe that would be fun?
The code below calls the function getNum and if the function returns true it prints "yes".
  void setup(){
    size(400, 400);
}
void draw(){
 if (getNum (16)){
   println("yes"); 
 }
}

boolean getNum(int numIn){
  if (numIn > 10){
    return true; 
  } 
  else{
    return false; 
  }
}
Use a class to make hundreds of individual bouncing balls by converting this code from Open Processing.

  /* OpenProcessing Tweak of *@*http://www.openprocessing.org/sketch/52767*@* */

  color g = color(200, 100, 200);
  float x =400;
  float y = 400;
  float acceleration = 10;
  float gravity = 0.15;

  void setup() {
    size(800, 600);
    background(255);
    smooth();
}

void draw() {
  background(255);
  makeball();
  y = y+acceleration; 
  acceleration += gravity; 
  if (y >height- 20) {
    acceleration = acceleration * -0.9;
  }
}

void makeball() {
  ellipse(x, y, 40, 40);
  fill(g);
  smooth();
}

Recursion

Recursion is a script that calls itself. You can get some trippy fractal-like sketches with it, but it's also easy to make something that crashes if you don't define a point at which it stops. My all-time favorite internet easter egg is to Google "recursion". Do it.
void setup() {
  size(640,360);
}
 
void draw() {
  background(255);
  drawCircle(width/2,height/2, 200);
}

void drawCircle(float x, float y, float radius) {
  ellipse(x, y, radius, radius);
  if(radius > 8) {
    drawCircle(x + radius/2, y + 5, radius/2);
    drawCircle(x - radius/2, y + 5, radius/2);
  }
}

Watch the Internet's Own Boy.

Totally lost? Try the Shiffman Yourube Channel