Constructor Parameters
Do you remember when we talked about functions in 2-5 we learned how adding parameters to functions makes them more general-purpose and reusable? Similarly, we can amp up the power of a class by adding parameters to the constructor() function such that when we instantiate an object we can specify custom starting values for its instance variables.
Let’s revise our constructor() by adding parameters:
1 2 3 4 5 6 7 8 |
class Ball { constructor(x, y, speed) { this.ballX = x this.ballY = y this.speedX = speed } … |
The variables we initialize inside the constructor() function are called instance variables because every instance (i.e., object) of the class will have the same variables but can have different values associated with them. Any instance variable in a class must be prefixed with this. followed by the name of the instance variable. On line 3 (above), we are assigning the instance variable this.ballX the value of the parameter variable x. Similarly, on line 4 we do something similar for this.ballY.
Line 5 is interesting. You’ll notice that there is a speed parameter variable, and also an instance variable called this.speed. Because the instance variable speed is prefixed with this. it tells p5.js that this.speed is an instance variable and different than the speed parameter variable. For example, without this. line 5 would not make much sense:
speed = speed
This confusing line would actually reassign the value of the parameter variable speed to itself, changing nothing. It does not initialize an instance variable at all!
Now we can instantiate a customized Ball object in our setup() function such that we can define where our myBall starts on the canvas and which direction/speed it’s traveling in:
1 2 3 4 5 6 |
let myBall function setup() { createCanvas(400, 400) myBall = new Ball(100, 100, 2) } |
Creating Many Objects
But wait, there’s more!
We can also now instantiate as many customized Ball objects as we like with just a few additional lines of code:
1 2 3 4 5 6 7 8 9 10 |
let myBall1 let myBall2 let myBall3 function setup() { createCanvas(400, 400) myBall1 = new Ball(100, 100, 2) myBall2 = new Ball(200, 200, -3) myBall3 = new Ball(300, 300, 1) } |
And once we’ve done that, we can control each Ball object independently:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function draw() { background(220) myBall1.move() myBall2.move() myBall3.move() myBall1.bounce() myBall2.bounce() myBall3.bounce() myBall1.display() myBall2.display() myBall3.display() } |
Try making these adjustments to the Ball class code from our last lesson. Hopefully, you can see how powerful classes and objects are when it comes to code reuse. After defining one class we can instantiate as many objects (even thousands!) of that class as we desire. We don’t need to cut and paste the same function code over and over for each object.
A Quick Recap
Let’s review what we’ve learned: Object-Oriented Programming (OOP) allows us to simplify our setup() and draw() functions by moving global variables and functions into a class. The first letter of a class name is traditionally capitalized to distinguish it from a variable name. You can define as many classes as you need in one program.
We add functionality to a class by defining functions inside it, but the function keyword is not used within a class. Every class must have a constructor() function to initialize special variables called instance variables. By defining a constructor() function that takes parameters we can instantiate (create) objects with different values for their instance variables.
Each instance (object) of a class will have the same instance variables (properties) but can have different values for them. Instance variables are identified with the prefix this. in front of the instance variable name and are global to all functions within the class. We can instantiate as many objects of a class as we need.
When Should I Use OOP?
The short answer is: From now on and whenever you can!
I hope you can appreciate that Object-Oriented Programming is a powerful way to organize and reuse code. Until now our programs have been pretty simple, and using OOP techniques might have been overkill especially while learning the fundamentals.
But from this point on we’ll find more and more situations where defining classes and instantiating objects will save us a lot of extra coding work. After all, every visual object that you draw on the canvas can be thought of as a graphical object. This makes OOP and coding in p5.js a natural fit!
Tip of the Iceberg
In these last two lessons we’ve looked at some of the basic concepts of Object-Oriented Programming, and for our purposes, this is enough. But what I’ve shown you is just the beginning! We won’t get into these advanced details in this p5.js tutorial. I cover OOP in much more detail in my intermediate (Python) and advanced (Java) programming tutorials.
You Try!
-
- Customize your enhanced version of the Ball class from Q1 last lesson such that the starting (x,y) location, RGB colour values, and x and y directions/speeds can be customized via constructor parameters. Try to add as many other instance variables as you can; for example, stroke weight. Instantiate 5 unique Ball objects to demonstrate your features.
- Getting ready for your Summative Project: Go back to 1-11 Q4 (or Q5) where you created an interactive PacMan controlled by the keyboard. Modify your solution so that all of the variables and functions related to PacMan are in a class. Your PacMan class should have functions to goUp(), goLeft(), goRight(), goDown(), update() and checkEdges(). The update() function should change the position of PacMan, and checkEdges() should check if PacMan has reached the edge of the window and make him appear on the other side. Use your class to instantiate a pacMan object. Outside the class, in addition to setup() and draw(), include a keyPressed() function to control the direction of your pacMan object by calling its functions.