Final Project

Concept and Artistic Vision

For my final assignment, I knew I wanted to incorporate many of the concepts we learned in class. I decided to use Perlin noise for the background to create realistic moving clouds, and use a class to create birds and control their movement using flocking methods such as alignment and cohesion. Additionally, I used fractals to create a tree.

To create the birds, I was greatly inspired by the image above. I used quadraticVertex to create a simple, but effective, silhouette. From here, I created a changing sky to make the sketch more dynamic and interactive. For the tree, I changed the radians value to make it more asymmetrical and realistic. Next, I applied a wind force to the birds, sky, and tree. When the user moves the slider towards the right, the clouds move faster, the birds flap more and fly slower, showing that they are struggling against the wind. Additionally, the tree sways slightly. there is a gradual change in all three elements when the slider is moved to the left, and when it is all the way left, the tree is completely still, the clouds move slowly, and the birds “soar” through the air. Finally, I figured out a way to make the birds interact with the tree. When the birds flying into the tree they “land” and “sit” on the tree. I added a “Birds” button for the user to introduce new birds into the environment.

Here is the inspiration, taken from a previous sketch I did for this class, for the background.

Embedded Sketches

Draft 1

Draft 2

Final Result 

User Testing

I asked a friend to test the second draft I linked in this post, and they found it to be a bit simple. When they were testing, they thought the birds would interact with the tree. From their feedback, I implemented the interaction with the tree. Other than that, I was told the buttons and the slider were very straightforward. However, since making the final version, I think the slider may be less accessible because the user has to click to generate more birds and also use the slider to modify the birds. I think a fix for this could be making a key-bind for the bird generation.

C0ding Translation and Logic

class Bird {
  constructor(x, y, birdSize, birdSpeed) {
    this.x = x;
    this.y = y;
    this.birdSize = birdSize;
    this.baseSpeed = birdSpeed;
    this.flapAngle = 0;
    this.flapSpeed = random(0.16, 0.18);
    this.stopped = false;
    this.flockingRadius = 100; // Radius within which birds influence each other
    this.wingOffset = random(0, TWO_PI); // Add an offset to stagger the wing flap
  }

  update(windFactor) {
    if (!this.stopped) {
      this.x -= this.baseSpeed / windFactor; // Birds move left
      if (this.x <= 100) {
        this.stopped = true; // Stop the bird when it reaches the tree
        // Add a small random offset to the x position to make birds stop at varied locations
        this.x = 100 + random(0, 70);
      }
      this.flapAngle += this.flapSpeed * windFactor;

      // Apply subtle flocking behavior if there are multiple birds
      if (birds.length > 1) {
        let cohesionForce = createVector(0, 0);
        let count = 0;

        for (let otherBird of birds) {
          if (otherBird !== this) {
            let d = dist(this.x, this.y, otherBird.x, otherBird.y);
            if (d < this.flockingRadius) {
              cohesionForce.add(createVector(otherBird.x - this.x, otherBird.y - this.y));
              count++;
            }
          }
        }

        if (count > 0) {
          cohesionForce.div(count); // Average position of nearby birds
          cohesionForce.setMag(0.3); // Increase the magnitude for more noticeable movement
          this.x += cohesionForce.x;
          this.y += cohesionForce.y;
        }
      }
    }
  }

  show() {
    push();
    translate(this.x, this.y);
    scale(this.birdSize);
    fill(0, 0, 0, 220);
    if (this.stopped) {
      this.drawSittingBird();
    } else {
      this.drawFlyingBird(sin(this.flapAngle + this.wingOffset));
    }
    pop();
  }

  drawFlyingBird(curveFactor) {
    let wingY = map(curveFactor, -1, 1, 0, -35);
    let wingCurve = map(curveFactor, -1, 1, -25, -50);

    // Left wing
    beginShape();
    vertex(0, 0);
    quadraticVertex(-25, -10, -35, wingY);
    quadraticVertex(-25, wingCurve, 0, 0);
    endShape(CLOSE);

    // Right wing
    beginShape();
    vertex(0, 0);
    quadraticVertex(40, -15, 70, wingY);
    quadraticVertex(40, wingCurve, 0, 0);
    endShape(CLOSE);
  }

  drawSittingBird() {
    // Only draw the right wing in the sitting position
    let wingY = 35; // Downward position (mirroring the wings up)
    let wingCurve = 50; // Gentle downward curve (mirroring wings up)

    // Right wing
    beginShape();
    vertex(0, 0);
    quadraticVertex(40, 15, 70, wingY);
    quadraticVertex(40, wingCurve, 0, 0);
    endShape(CLOSE);
  }
}

The Bird class initializes the bird’s properties, such as its position, size , and base movement speed . The bird’s wings are animated through the flapAngle, which starts at 0, and flapSpeed, which is randomly set between 0.16 and 0.18, simulating the rate of wing flapping. The flockingRadius defines how close other birds need to be for the bird to be influenced by their positions, and wingOffset staggers the wing flapping of different birds, creating a more natural look in a group of birds. The update method updates the birds position as it moves depending on the windFactor, which is calculated through the slider. I added cohesionForce so if the birds are in a certain radius of one another, the birds begin to group. In the show method, I used quadratic Bezier curves, giving the wings a smooth, realistic shape. For when the birds reach the tree, only their right wing is rendered to create a sitting position.

Challenges

I struggled with handling all the different elements of my code, and with certain elements clashing/interfering with each other. This meant I had to debug very slowly with the console log. The biggest challenge was creating the birds and their movements, as I wanted them to look as realistic as possible.

Future Improvements 

In the future, I would have perfected parts of the code more. The birds just stop when they reach the tree, so when the tree is moving, the birds do not move with it. I wasn’t sure how to approach troubleshooting this, but I think I would have to do much more complicated boundary definitions for the tree. Additionally, I tried to create a “stop watching” button to stop the simulation and go back to the start screen, but this was interfering with different elements of my code, so I deleted it. I also tried really hard for one of the buttons, such as the “day” or “night” button to cause the birds in the tree to resume flying. Nothing I tried worked. I feel like as the simulation goes on, the birds are too clustered on the tree. Another improvement I would make is add music or nature sounds to the background.

Leave a Reply

Your email address will not be published. Required fields are marked *