setup()
setup() runs once at sketch startup. Here it initializes the canvas, creates the fish object with all its methods (which p5.js calls when fish.show() and fish.update() are invoked), and sets up the sound oscillators. Notice the fish object uses 'this' to reference its own properties—a key pattern in object-oriented game programming.
🔬 The fish is drawn with an ellipse body and triangle tail. What happens if you change FISH_SIZE * 1.5 to FISH_SIZE * 3 in the ellipse? What about the tail positions?
// Main body of the fish (ellipse)
ellipse(this.x, this.y, FISH_SIZE * 1.5, FISH_SIZE);
// Tail of the fish (triangle)
triangle(this.x - FISH_SIZE * 0.75, this.y,
this.x - FISH_SIZE * 1.5, this.y - FISH_SIZE / 2,
this.x - FISH_SIZE * 1.5, this.y + FISH_SIZE / 2);
function setup() {
createCanvas(1366, 768); // Match user's screen size
background(0, 191, 255); // Initial underwater blue background
// Initialize fish object
fish = {
x: width / 4, // X-position of the fish (quarter way across the screen)
y: height / 2, // Initial Y-position (center of the screen)
vy: 0, // Vertical velocity of the fish
// Method to draw the fish
show: function() {
fill(255, 165, 0); // Orange color for the fish
noStroke();
// Main body of the fish (ellipse)
ellipse(this.x, this.y, FISH_SIZE * 1.5, FISH_SIZE);
// Tail of the fish (triangle)
triangle(this.x - FISH_SIZE * 0.75, this.y,
this.x - FISH_SIZE * 1.5, this.y - FISH_SIZE / 2,
this.x - FISH_SIZE * 1.5, this.y + FISH_SIZE / 2);
},
// Method to update the fish's position and velocity
update: function() {
this.vy += GRAVITY; // Apply gravity to vertical velocity
this.y += this.vy; // Update y-position based on velocity
// Constrain fish to stay within the canvas height
this.y = constrain(this.y, FISH_SIZE / 2, height - FISH_SIZE / 2);
// Check if fish hits the top or bottom of the screen
if (this.y === FISH_SIZE / 2 || this.y === height - FISH_SIZE / 2) {
hitSound.amp(0.5, 0.1); // Play hit sound
setTimeout(() => hitSound.amp(0, 0.1), 100); // Fade out sound
gameState = GAME_OVER; // End the game
}
},
// Method to make the fish jump
jump: function() {
this.vy = JUMP_FORCE; // Set negative velocity to move upwards
jumpSound.amp(0.5, 0.1); // Play jump sound
setTimeout(() => jumpSound.amp(0, 0.1), 100); // Fade out sound
},
// Method to check for collision with an obstacle
hits: function(obstacle) {
// Define the fish's bounding box for collision detection
let fishRect = {
x: this.x - FISH_SIZE * 0.75, // Left edge of the fish's body
y: this.y - FISH_SIZE / 2, // Top edge of the fish's body
width: FISH_SIZE * 1.5,
height: FISH_SIZE
};
// Define the top obstacle's bounding box
let obstacleTopRect = {
x: obstacle.x,
y: 0,
width: obstacle.width,
height: obstacle.y1 // y1 is the height of the top obstacle
};
if (checkRectCollision(fishRect, obstacleTopRect)) {
return true; // Collision with top obstacle
}
// Define the bottom obstacle's bounding box
let obstacleBottomRect = {
x: obstacle.x,
y: obstacle.y2, // y2 is the y-position of the bottom obstacle
width: obstacle.width,
height: height - obstacle.y2 // Height from y2 to the bottom of the canvas
};
if (checkRectCollision(fishRect, obstacleBottomRect)) {
return true; // Collision with bottom obstacle
}
return false; // No collision
}
};
// Create simple oscillator sound effects using p5.sound
// These will be used for jump, hit, and score events.
jumpSound = new p5.Oscillator();
jumpSound.setType('sine'); // Sine wave for a smooth jump sound
jumpSound.freq(660); // Higher frequency for a distinct jump
jumpSound.amp(0); // Start silent
jumpSound.start(); // Start the oscillator
hitSound = new p5.Oscillator();
hitSound.setType('sawtooth'); // Sawtooth wave for a harsher hit sound
hitSound.freq(220); // Lower frequency for a 'thud'
hitSound.amp(0);
hitSound.start();
scoreSound = new p5.Oscillator();
scoreSound.setType('triangle'); // Triangle wave for a pleasant score chime
scoreSound.freq(880); // High frequency for a clear chime
scoreSound.amp(0);
scoreSound.start();
userStartAudio(); // Enable audio, required for web audio to work in browsers
}
Line-by-line explanation (16 lines)
🔧 Subcomponents:
fish = { x: width / 4, y: height / 2, vy: 0, ... }
Creates a fish object with position, velocity, and methods for drawing, updating, jumping, and collision detection
jumpSound = new p5.Oscillator(); jumpSound.setType('sine'); jumpSound.freq(660);
Initializes three p5.Oscillator objects with different wave types and frequencies for distinct audio feedback
createCanvas(1366, 768);- Creates a 1366×768 pixel canvas—a widescreen aspect ratio that gives plenty of space for obstacles and scrolling motion
background(0, 191, 255);- Sets the initial background to a sky-blue color (RGB: 0, 191, 255), creating an underwater or sky theme
fish = {- Begins creation of the fish object that will hold the fish's position, velocity, and all its behavior methods
x: width / 4,- Places the fish one-quarter of the way across the screen horizontally, leaving room for obstacles to approach from the right
y: height / 2,- Starts the fish at the vertical center of the canvas—a neutral starting position before gravity pulls it down
vy: 0,- Initializes vertical velocity to 0—the fish is not moving up or down when the game starts
ellipse(this.x, this.y, FISH_SIZE * 1.5, FISH_SIZE);- Draws the fish's main body as an orange ellipse, 1.5 times wider than it is tall to create a fish-like shape
triangle(this.x - FISH_SIZE * 0.75, this.y, this.x - FISH_SIZE * 1.5, this.y - FISH_SIZE / 2, this.x - FISH_SIZE * 1.5, this.y + FISH_SIZE / 2);- Draws a triangular tail on the left side of the fish, pointing backward—the three points create a classic fish silhouette
this.vy += GRAVITY;- Adds the GRAVITY constant to the fish's vertical velocity each frame, making it accelerate downward realistically
this.y += this.vy;- Updates the fish's y position by adding its velocity—higher velocity means bigger downward movement each frame
this.y = constrain(this.y, FISH_SIZE / 2, height - FISH_SIZE / 2);- Clamps the fish's y position between upper and lower bounds so it cannot move outside the canvas vertically
if (this.y === FISH_SIZE / 2 || this.y === height - FISH_SIZE / 2) {- Checks if the fish has reached either the top or bottom edge—if true, the fish has hit a boundary and the game ends
jumpSound = new p5.Oscillator();- Creates a new oscillator object that will be connected to the speaker to produce sounds when the fish jumps
jumpSound.setType('sine');- Sets the oscillator to use a sine wave, which produces a smooth, pure tone ideal for a jump sound effect
jumpSound.freq(660);- Sets the oscillator frequency to 660 Hz, a high-pitched tone that signals the jump action to the player's ear
userStartAudio();- Enables web audio in the browser—required because modern browsers require user interaction before audio can play