Concept:
My concept goes back to Dory from Finding Nemo. She swims around and tries to find Nemo, but she will never find him in this universe I created. With a constant restart whenever she goes off the screen.
Code:
let fish; // Declaring a variable for the fish object
function setup() {
createCanvas(600, 400);
fish = new Fish(); // Initializes the fish object
}
function draw() {
background(0, 150, 255); // Ocean blue background for a feeling of thr sea
fish.update(); // Updates the fish's position and movement
fish.display(); // Display the fish on the canvas
}
class Fish {
constructor() {
// Initialize the fish's properties: position, velocity, and acceleration
this.position = createVector(width / 2, height / 2); // Starts the fish in the middle of the canvas
this.velocity = createVector(2, 0); // Give the fish an initial velocity to move it to the right
this.acceleration = createVector(0.01, 0); // Adds a small acceleration to simulate swimming
this.angle = 0; // Angle for tail movement
}
update() {
// This function updates the fish's position and behavior each frame
// Simulate natural swimming by adding small random changes to the fish's acceleration
this.acceleration = createVector(random(-0.01, 0.01), random(-0.01, 0.01));
// Add the acceleration to the velocity to change the fish's speed
this.velocity.add(this.acceleration);
// Limits the maximum speed of the fish to make the movement smoother
this.velocity.limit(3);
// Updates the fish's position by adding the velocity to it
this.position.add(this.velocity);
// If the fish moves off one edge of the canvas, make it reappear on the opposite edge
if (this.position.x > width) this.position.x = 0; // Reappear on left side if off right
if (this.position.x < 0) this.position.x = width; // Reappear on right side if off left
if (this.position.y > height) this.position.y = 0; // Reappear on top if off bottom
if (this.position.y < 0) this.position.y = height; // Reappear on bottom if off top
// Creates an oscillating angle for the fish's tail using the sine function
// The angle swings back and forth, making the tail move naturally
this.angle = sin(frameCount * 0.1) * PI / 6;
}
display() {
// This function draws the fish on the canvas
// Set the fill color to blue for Dory's body
fill(0, 100, 255); // Blue body color
stroke(0); // Black outlines
strokeWeight(1); // Set the line thickness for the outline
push(); // Starts a new drawing state to control the position and rotation of the fish
translate(this.position.x, this.position.y); // Moves the fish to its current position
rotate(this.velocity.heading()); // Rotates the fish in the direction of its movement
// Draws the fish's body as an ellipse
ellipse(0, 0, 50, 20); // The fish's body (50 pixels wide, 20 pixels tall)
// Draws the fish's tail as a triangle that moves back and forth
// Move the tail to the back of the body and rotate it based on the oscillating angle
push(); // Starts a new drawing state for the tail
translate(-25, 0); // Moves the tail to the back end of the fish
rotate(this.angle); // Rotates the tail back and forth
fill(255, 215, 0); // Yellow color for Dory's tail
triangle(0, -10, -20, 0, 0, 10); // Draw the tail as a triangle
pop(); // Restore the previous drawing state for the body
// Draw Dory's fins (small yellow triangles)
fill(255, 215, 0); // Yellow fin color
triangle(10, -10, 20, 0, 10, 10); // Top fin
triangle(0, 5, -10, 10, -5, 5); // Bottom fin
// Draw black markings on the body
fill(0); // Black for markings
beginShape(); // Start shape for the black marking
vertex(0, -10);
vertex(10, -5);
vertex(0, 5);
vertex(-10, 0);
endShape(CLOSE); // Create a simple black marking shape
// Draws the fish's eye as a small white circle with a black pupil
fill(255); // White for the eye
ellipse(15, -5, 8, 8); // Draw the eye (8x8 pixel circle)
fill(0); // Black pupil
ellipse(15, -5, 4, 4); // Draw the pupil (4x4 pixel circle)
pop(); // Restores the previous drawing state
}
}
In this simulation, the fish moves by updating its position based on velocity and acceleration, which makes its motion feel natural and fluid, like a real fish swimming. The velocity determines how fast and in which direction the fish swims, while the acceleration adds subtle variations, simulating the fish’s changing speed and slight adjustments in its path. Creating a decent-looking fish was tricky because I had to balance the simplicity of shapes (an ellipse for the body, triangles for the tail, and fins) while ensuring it still resembled a fish, particularly Dory from Finding Nemo. I also added some black markings and a wiggling tail to make the movement more realistic.
One fun complication was ensuring the fish didn’t just swim off the canvas and disappear forever.
// If the fish moves off one edge of the canvas, make it reappear on the opposite edge if (this.position.x > width) this.position.x = 0; // Reappear on the left side if off the right edge if (this.position.x < 0) this.position.x = width; // Reappear on the right side if off the left edge if (this.position.y > height) this.position.y = 0; // Reappear at the top if off the bottom edge if (this.position.y < 0) this.position.y = height; // Reappear at the bottom if off the top edge
Horizontal boundaries:
If the fish’s x position goes beyond the right edge (this.position.x > width), it reappears at the left edge by setting this.position.x = 0.
Similarly, if it swims past the left edge (this.position.x < 0), it reappears on the right by setting this.position.x = width.
Vertical boundaries:
If the fish swims off the bottom (this.position.y > height), it reappears at the top by setting this.position.y = 0.
If it goes above the top (this.position.y < 0), it reappears at the bottom by setting this.position.y = height.
This wrapping behavior is achieved by checking if the fish’s position exceeds the canvas boundaries and resetting its position to the opposite side, ensuring Dory never swims too far away!
Sketch:
Reflection:
I would love to know more about how to create better-looking shapes without that much struggle; while the end results look like fish, they aren’t the best out there. Also, I would like to add a little controller to make the fish move around.
Resources:
- https://p5js.org/reference/p5/p5.Vector/
- https://p5js.org/reference/p5/sin/
- https://p5js.org/reference/p5/frameCount/#:~:text=A%20Number%20variable%20that%20tracks,in%20draw()%20finishes%20executing.
- https://forum.processing.org/two/discussion/16493/daniel-shiffman-s-nature-of-code-exercise-1-5.html