Introduction to Object Oriented Modeling

Object Oriented Modeling

Object Oriented Modeling - Overview

  • Extending the Jitter Class

    Understanding the class structure

  • Custom Constructors Challenge

    Understanding constructors and applying complex classes

  • Eye Class Extension

    Further exercises on classes

Working with Mulitple Elements on Screen


So far, when we wanted several versions of a function to run we needed to create a set of related global variables to store their states. For example, a series of arrays would be needed to store x, y and maybe a size state and they all needed to be accessed via their corresponding index. So something like this would not be uncommon to see ellipse(x[i], y[i], size[i]). Each index position (i) corresponds with a saved coordinate and size.


              let x = [];
              let y = [];
              let size = [];
              
              function setup() {
                createCanvas(windowWidth, windowHeight);

                for(let i = 0; i < 3; i++) {
                  x[i] = random(width);
                  y[i] = random(height);
                  size[i] = random(50,100);
                }
              }

              function draw() {
                for(let i = 0; i < 3; i++) {
                  ellipse(x[i], y[i], size[i]);
                }
              }
              

Working with Mulitple Elements on Screen


Let's say that we want multiple target functions with different parameter values changing over time, we would have to declare a few array variables to store all of this changing data. This is fine in the context of the target function as it does not require many variables however when working with complex functions this quickly becomes an onerous task to update and maintain so many related arrays. This is where classes become quite useful.


              let x = [];
              let y = [];
              let size = [];
              
              function setup() {
                createCanvas(windowWidth, windowHeight);
                for(let i = 0; i < 6; i++) {
                  // Initalise state for target
                  x[i] = random(width);
                  y[i] = random(height);
                  size[i] = random(50,100);
                }
              }

              function draw() {
                for(let i = 0; i < 6; i++) {
                  target(x[i], y[i], size[i],5);
                  // Update Coordinates
                  x[i]+=random(-1,1);
                  y[i]+=random(-1,1);
                }
              }
              // Target function
              

Object Oriented Modelling


An Object-oriented approach makes the process of managing variables and functions simpler at a larger scale. Object-oriented programming uses what we call classes as blueprints for complex elements with several behaviors or "methods". A class is a definition of methods (functions) and variables and an object is a single instance of a class. We will be using three terms, method, object, and class a lot so its important to understand the definition of each.

For our purposes, all Classes require a constructor and at least one method.

We will expand upon this in further slides

        
        // Create the class
        class example {
          // think of the constructor as the 'setup' of our class
          // this runs once and this is where we declare object-specific variables
          constructor() {
            // Numbers specific to our object are declared using the this notation
            // This means that the number is specific to that specific object
            this.x = random(width);
            this.y = random(height);
          }
          // this is where we write logic or render to screen
          method() {
            // we can use our object specific numbers
            ellipse(this.x, this.y, 50);
          }
        }
        

Apple Class Example


  • Name: Apple
  • Fields: X coordinate, Y coordinate, Size
  • Methods: grow(), fall(), rot()

The grow() method that increases the size field of the apple when it is spawned in the environment.
The fall() method is used to check the size and once above a randomly decided size Once this is true the apple will stop growing and fall to the ground.
The rot() method checks if the apple is on the ground and begins to decrease the value of the size field and change the colour to black.

Once this process is complete the apple object is removed and replaced with a new one.

Creating a Class


Here we will walk through how to structure a class. We will start basic so we can focus on the fundamentals structures of a class. In the example below, we have an ellipse with a random position and size. Let us create an equivalent but with a class structure.


              // Global variables used to store the ellipse state
              let x, y;
              let diameter = 100;

              function setup() {
                createCanvas(windowWidth, windowHeight);
                noStroke();
                x = random(diameter, width - diameter);
                y = random(diameter, height- diameter);
              }

              function draw() {
                background(230);
                ellipse(x, y, diameter, diameter);
              }
              

Defining Our Class


Here is what our class looks like to achieve the same result as the previous slide. We have called our class spot and given it its own functions which we are calling using the dot syntax. We have effectively grouped all of the related variables and functions into a single class called is Spot. In this example, all values required by the display method are created in the constructor. You'll notice a new syntax, the this keyword.


              class Spot {
                // This is our constructor where all parameters are defined
                constructor() {
                  // Assign random to diameter
                  this.diameter = random(50,100);
                  // Assign random number based on diameter
                  this.x = random(this.diameter, width - this.diameter);
                  // Assign random number based on diameter
                  this.y = random(this.diameter, height - this.diameter);
                }

                // here we are defining our method
                display() {
                  fill(50);
                  ellipse(this.x, this.y, this.diameter, this.diameter);
                }

              }
              

This. Keyword


A new bit of syntax that will use in the context of classes is this keyword. When we use it in our class we are referring to the instance of the variable that belongs to that current object being run. For example, in the snippet of code below, we create variables x, y, and diameter. These are referenced by the display method, in the ellipse function. So if we have multiple instances of the Spot class all of the random numbers created for it are used to draw to screen.


          class Spot {
            constructor() {
              this.diameter = random(50,100);
              this.x = random(this.diameter, width - this.diameter);
              this.y = random(this.diameter, height - this.diameter);
            }
          
            display() {
              fill(50);
              // this. referes to the varaibles declared for this instance of the object
              ellipse(this.x, this.y, this.diameter, this.diameter);
            }
          
          }
          

Creating an Object with our Spot Class


Here is what the syntax looks like when we create an object of a class and call upon a method to draw onto the screen.


              // Declare a variable to store an object
              let spot1;

              function setup() {
                createCanvas(windowWidth, windowHeight);
                noStroke();

                // Construct the object
                spot1 = new Spot(); 
              }

              function draw() {
                background(0);
                // Using the dot operator call the display method in the spot1 object
                spot1.display();
              }
              

Dot Syntax


The fields and methods of an object are accessed with the dot operator. The syntax spot1.diameter accesses the value of the diameter field inside the spot1 object. To run the display() method inside the spot1 object, the syntax spot.display() is used.


              let spot1;
              function setup() {
                createCanvas(windowWidth, windowHeight);
                noStroke();
                spot1 = new Spot();
              }

              function draw() {
                background(230);
                // Run the display method of spot1
                spot1.display();
                
                // returns the requested parameter
                let spotSize = spot1.diameter;
              }

              // Spot class
              

Extending the Spot Class


The Spot class can now be extended to support more behaviors by adding speed and direction fields to maintain information about movement and adding a move() method to update the position of a Spot object when called.


              class Spot {
                constructor() {
                  this.diameter = random(50,100);
                  this.x = random(this.diameter, width - this.diameter);
                  this.y = random(this.diameter, height - this.diameter);
                  this.speed = random(3);
                }
          
                display() {
                  fill(50);
                  ellipse(this.x, this.y, this.diameter, this.diameter);
                }
          
                // This method will make our spot bounce off the window edges
                move() {
                  // Updates the objects x with the objects speed
                  this.x += this.speed;
                  // Tests to see if the ellipse has touched the size
                  if ((this.x > (width - this.diameter / 2)) || (this.x < this.diameter / 2)) { // Reverses the direction if true
                    this.speed *=-1; 
                  } 
                } 
              } 
              

Spot Class Extended



          let spot1;
          function setup() {
            createCanvas(windowWidth, windowHeight);
            noStroke();
            spot1 = new Spot();
          }
          
          function draw() {
            background(230);
            spot1.display();
          }
          
          class Spot {
            constructor() {
              this.diameter = random(50,100);
              this.x = random(this.diameter, width - this.diameter);
              this.y = random(this.diameter, height - this.diameter);
              this.speed = random(3);
            }
          
            display() {
              fill(50);
              ellipse(this.x, this.y, this.diameter, this.diameter);
            }
          
            move() {
              this.x += this.speed;
              if ((this.x > (width - this.diameter / 2)) || (this.x < this.diameter / 2)) {
                this.speed *=-1; 
              } 
            } 
          }
          

Arrays of Objects


Working with arrays of objects is similar to working with arrays of other data types. Because each array element is an object, each element of the array must be created before it can be accessed. The steps for working with an array of objects are:

  1. Declare the array globally
  2. Create a new object for each position in the array
  3. Call the required methods for that object to run as intended

              let spot = [];

              function setup() {
                createCanvas(windowWidth, windowHeight);
                noStroke();

                // Filling the array with spot objects
                for (let i = 0; i < 3; i++) {
                  spot[i]=new Spot();
                } 
              }
              
              function draw() {
                background(230); 

                // Use a for loop to run through every object method
                for (let i=0; i < spot.length; i++) {
                  spot[i].display();
                  spot[i].move();
                } 
              }
              

Extending the Jitter Class Challenge


Here

Defining Our Constructor Parameters


In this example, we have set a parameter for xPos, yPos and dia notice how these numbers are then allocated to specific variables declared inside the class.


              class Spot {
                // All of these parameters get applied to our class fields
                // When we create a new object.
                constructor(xPos, yPos, dia, speed) {
                  this.x = xPos; // Assign xpos to x
                  this.y = yPos; // Assign ypos to y
                  this.diameter = dia; // Assign dia to diameter
                  this.speed = speed; 
                }

                display() {
                  fill(50);
                  ellipse(this.x, this.y, this.diameter, this.diameter);
                }

                // This method will make our spot bounce off the window edges
                move() {
                  this.x += this.speed;
                  if ((this.x > (width - this.diameter / 2)) || (this.x < this.diameter / 2)) {
                    this.speed *=-1;
                  }
                }
              }
              

Defining Our Constructor Parameters


Now that we have custom constructor parameters, when creating a new object we also need to specify these numbers. In our example, we need to set a coordinate, size and speed to create a new Spot object. Custom parameters are helpful when you want to set up your class in a certain way.


              let spot;

              function setup() {
                createCanvas(windowWidth, windowHeight);
                noStroke();
                let size = random(50,100);
                let speed = random(3);
                spot = new Spot(random(size, width - size), random(size, height - size), size, speed);
              }

              function windowResized() {
                resizeCanvas(windowWidth, windowHeight);
                setup();
              }

              function draw() {
                background(230);
                spot.display();
                spot.move();
              }

              // Spot Class
              

Custom Constructors Challenge


Here

Updating Object Methods with External Values


It's fairly common that you will need to have contextual information only available in the draw loop being required by a specific method in your class. The syntax for this is familiar. Here we use the eyes class as an example, this class requires a current mouseX and mouseY coordinate to function properly.


              // In the draw loop
              for (var i = 0; i < eyes.length; i++) { 
                eyes[i].update(mouseX, mouseY); 
                eyes[i].display(); 
              }

              // In the eyes class
              // The update method has the parameters it needs declared in its parentheses
              update(mouseX, mouseY) {
                // etc
                // etc
              }

              

ES6 vs ES5


We are teaching a more current version of javascript which is called ES6 while watching older tutorial videos you'll notice that there is a subtle difference between what you'll see in class and the content you'll consume. Here is a side by side comparison of ES6 and ES5 so you can see the difference between the two. At this current point in time, both of these syntaxes work just fine.

Newer


              class Spot {
                // All of these parameters get applied to our class fields
                // When we create a new object.
                constructor(xPos, yPos, dia, speed) {
                  this.x = xPos; // Assign xpos to x
                  this.y = yPos; // Assign ypos to y
                  this.diameter = dia; // Assign dia to diameter
                  this.speed = speed; 
                }

                display() {
                  fill(50);
                  ellipse(this.x, this.y, this.diameter, this.diameter);
                }

                // This method will make our spot bounce off the window edges
                move() {
                  this.x += this.speed;
                  if ((this.x > (width - this.diameter / 2)) || (this.x < this.diameter / 2)) {
                    this.speed *=-1;
                  }
                }
              }
              

Older


                
               // Spot would be defined as a function with the parameters being declared here as well.
              function Spot(xpos, ypos, dia, speed) {
                // the constructor isn't declare and fields are 
                // just stated inside the spot class block
                this.x = xPos;
                this.y = yPos;
                this.diameter = dia;
                this.speed = speed;

                this.display = function() {
                  fill(50);
                  ellipse(this.x, this.y, this.diameter, this.diameter);
                }

                // This method will make our spot bounce off the window edges
                this.move = function() {
                  this.x += this.speed;
                  if ((this.x > (width - this.diameter / 2)) || (this.x < this.diameter / 2)) {
                    this.speed *=-1;
                  }
                }
              }
              

Eyes Extension Challenge


Here