Assignment 3: OOP-Based Art

Concept

Based on the ability to create multiple objects with ease using OOP and arrays, I envisioned colorful schools of fish swimming in the sea for my third assignment. One that looked something like:

Image courtesy of my Notes app.

Coding Highlights

I first wanted to create a still picture before adding in the movement. Before coding in the fish, I sought to paint in the scenery they would soon inhabit. A highlight here was managing to give the sky and sea a nice gradient, which I achieved with the following code:

//Canvas size is 600 by 600.
//Sunset Sky
for (let i = 0; i < 200; i = i + 2){
  fill(255, 170 - i/1.5, 85);
  rect(0, i, 600, 5);
}
//Sea
for (let i = 0; i < 400; i = i + 4){
  fill(100 - i/4, 130 - i/4, 255);
  rect(0, i + 200, 600, 5);
}
//Sun
fill(255, 190, 40);
arc(300, 200, 200, 200, radians(180), radians(0));

Using two for() loops (one for the sky and the other for the sea), I painted in thin rectangles that slightly darken with each iteration of the loop, giving off the impression of a gradient. Upon the sunset sky, I drew in the setting sun using a simple arc function. Thus the scenery was complete, waiting for the fish to move in. Time to make these schools of fish an actual class.

//Create Class for Fish
class Fish{
  constructor(){
    this.xPos = random(0, 600);
    this.yPos = random(230, 600);
  }
  //Function for Drawing Fish
  draw(){
    fill(240, 145, 40);
    //Body
    ellipse(this.xPos, this.yPos, 60, 30);
    //Tail
    triangle(this.xPos + 10, this.yPos, this.xPos + 40, this.yPos - 15, this.xPos + 40, this.yPos + 15);
    //Eye
    fill(0);
    ellipse(this.xPos - 20, this.yPos - 4, 5);
    //Smile
    arc(this.xPos - 30, this.yPos, 15, 7, radians(0), radians(90));
  }
}

With this class established, I could now make objects that could call upon the draw() function described in the class to draw in fish at random locations. I churned out these objects by creating an array, generating multiple values—and in turn objects—within the array using a for() loop in the setup() function, and then calling upon said objects with another for() loop in the draw() function:

//Only Included Code Pertaining to Fish Creation
//Create Array for Fish
let fish = [];

function setup() {
  createCanvas(600, 600);
  noStroke();
  //Generate Multiple Values in Array for Fish
  for (let i = 0; i < 10; i++){
    fish[i] = new Fish();
  }
}

function draw() {
  //Draw In Fish
  for (let i = 0; i < 10; i++){
    fish[i].draw();
  }
}

With these ingredients, the still-picture draft was done!

A school of 10 nice fish. This assignment was going swimmingly.

Now, to make the fish move. This meant that I would have to add a new function into the Fish() class. I did so by creating and using two new variables, this.xSpeed and this.ySpeed, for which I assigned random values to make the fish swim at different speeds.

//Only Included Movement Part of the Class

//Create Class for Fish
class Fish{
  constructor(){
    this.xPos = random(0, 600);
    this.yPos = random(230, 580);
    //New Variables for Fish Movement
    this.xSpeed = random(-5, -1);
    this.ySpeed = random(-2, 2);
  }
  //New Function for Fish Movement
  move(){
    this.xPos += this.xSpeed;
    //Check to Stop Fish from Leaving Water
    if (this.yPos > 230){
      this.yPos += this.ySpeed;
    }
    else {
      this.ySpeed = random(0, 1);
      this.yPos += this.ySpeed;
    }
    //Check to Stop Fish from Staying Below Frame
    if(this.yPos < 610){
      this.yPos += this.ySpeed;
    }
    else {
      this.ySpeed = random(-1, 0);
      this.yPos += this.ySpeed;
      }
    }
}

As described in the comments, this code continually moves the position of the fish and stops them from moving out of the water by checking for their y positions and adjusting their vertical “speed” as necessary.

To spice things up, I also decided to randomize the RGB values of the fish by making three new variables (for each value) within the constructor(), setting each of them to random(255), and incorporating them within the draw() function of the Fish() class.

All I needed now was one more function in the Fish() class, one that resets the position of the fish, to make them loop around back into the canvas:

//This function is within the Fish() class.

//Function for Resetting + Rerolling Values of Fish Out of Frame
reset(){
  if(this.xPos < -50){
    this.xPos = 650;
    this.yPos = random(230, 580);
    this.xSpeed = random(-5, -1);
    this.ySpeed = random(-2, 2);
    this.rValue = random(255);
    this.gValue = random(255);
    this.bValue = random(255);
  }

This code resets the x positions of the fish to the right of the canvas and ensures that they keep swimming within view. Their horizontal and vertical speeds, along with their RGB values, are also rerolled to keep things fresh. The fish were ready to swim.

There they go. You can click on the canvas to see them move smoothly!

And so they swam.

Reflections

Yet another fun assignment to work on! The thrill of watching your code work the way you intended never gets old. It really does feel like solving a puzzle that I’ve presented for myself, one that yields tangible results when solved.

If I had more time, I would have liked to make the fish stay within the water in a more natural way rather than bouncing off the surface boundary as they are now. The key here would be to devise a block of code that simulates acceleration/deceleration, a challenge to tackle at a later date for sure.

By working on this piece, I feel like I’ve gotten a better sense of OOP and a taste of what classes can do when paired with arrays. I eagerly look forward to the next step in our coding journey!

Leave a Reply