setup()
setup() runs once when the program starts. It initializes the canvas size, creates the fish object with all its methods (show, update, jump, shoot, hits), and prepares the five sound oscillators that the game will use. This is the perfect place to set up game entities and resources that don't change every frame.
function setup() {
createCanvas(1366, 768);
background(0, 191, 255);
fish = {
x: width / 4,
y: height / 2,
vy: 0,
show: function() {
fill(255, 165, 0);
noStroke();
ellipse(this.x, this.y, FISH_SIZE * 1.5, FISH_SIZE);
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);
fill(100);
rect(this.x + FISH_SIZE * 0.5, this.y - FISH_SIZE * 0.3, FISH_SIZE * 0.7, FISH_SIZE * 0.2);
},
update: function() {
this.vy += GRAVITY;
this.y += this.vy;
this.y = constrain(this.y, FISH_SIZE / 2, height - FISH_SIZE / 2);
if (this.y === FISH_SIZE / 2 || this.y === height - FISH_SIZE / 2) {
hitSound.amp(0.5, 0.1);
setTimeout(() => hitSound.amp(0, 0.1), 100);
gameState = GAME_OVER;
}
},
jump: function() {
this.vy = JUMP_FORCE;
jumpSound.amp(0.5, 0.1);
setTimeout(() => jumpSound.amp(0, 0.1), 100);
},
shoot: function() {
if (millis() - lastShotTime > SHOOT_COOLDOWN) {
let missile = {
x: this.x + FISH_SIZE * 1.2,
y: this.y - FISH_SIZE * 0.2,
speed: MISSILE_SPEED,
show: function() {
fill(255, 0, 0);
noStroke();
ellipse(this.x, this.y, MISSILE_SIZE);
},
update: function() {
this.x += this.speed;
},
offscreen: function() {
return this.x > width + MISSILE_SIZE;
}
};
missiles.push(missile);
shootSound.amp(0.5, 0.1);
setTimeout(() => shootSound.amp(0, 0.1), 100);
lastShotTime = millis();
}
},
hits: function(obstacle) {
let fishRect = {
x: this.x - FISH_SIZE * 0.75,
y: this.y - FISH_SIZE / 2,
width: FISH_SIZE * 1.5,
height: FISH_SIZE
};
let obstacleTopRect = {
x: obstacle.x,
y: 0,
width: obstacle.width,
height: obstacle.y1
};
if (checkRectCollision(fishRect, obstacleTopRect)) {
return true;
}
let obstacleBottomRect = {
x: obstacle.x,
y: obstacle.y2,
width: obstacle.width,
height: height - obstacle.y2
};
if (checkRectCollision(fishRect, obstacleBottomRect)) {
return true;
}
return false;
}
};
jumpSound = new p5.Oscillator();
jumpSound.setType('sine');
jumpSound.freq(660);
jumpSound.amp(0);
jumpSound.start();
hitSound = new p5.Oscillator();
hitSound.setType('sawtooth');
hitSound.freq(220);
hitSound.amp(0);
hitSound.start();
scoreSound = new p5.Oscillator();
scoreSound.setType('triangle');
scoreSound.freq(880);
scoreSound.amp(0);
scoreSound.start();
shootSound = new p5.Oscillator();
shootSound.setType('triangle');
shootSound.freq(1000);
shootSound.amp(0);
shootSound.start();
explodeSound = new p5.Oscillator();
explodeSound.setType('noise');
explodeSound.amp(0);
explodeSound.start();
userStartAudio();
}
Line-by-line explanation (21 lines)
🔧 Subcomponents:
fish = { x: width / 4, y: height / 2, ... }
Creates a single fish object that holds position, velocity, and methods for drawing, updating, jumping, shooting, and collision detection
show: function() { ellipse(...); triangle(...); rect(...); }
Draws the fish as an orange ellipse body, a triangle tail, and a grey missile launcher rectangle
update: function() { this.vy += GRAVITY; this.y += this.vy; ... }
Applies gravity to the fish's vertical velocity, moves the fish downward, constrains it to the canvas, and ends the game if it hits the top or bottom edge
jumpSound = new p5.Oscillator(); jumpSound.setType('sine'); ...
Creates five oscillator objects (jump, hit, score, shoot, explode) that generate synthesized sound effects on demand
createCanvas(1366, 768);- Creates a 1366x768 pixel canvas, the main drawing area for the game
background(0, 191, 255);- Fills the initial canvas with a cyan/sky blue color (RGB: 0, 191, 255) to simulate an underwater or sky background
fish = {- Begins the creation of a fish object that will hold all the fish's properties and methods
x: width / 4,- Sets the fish's starting x-position to one-quarter of the canvas width from the left edge
y: height / 2,- Sets the fish's starting y-position to the vertical center of the canvas
vy: 0,- Initializes the fish's vertical velocity to 0 (not moving up or down at the start)
ellipse(this.x, this.y, FISH_SIZE * 1.5, FISH_SIZE);- Draws the fish's main body as an ellipse 1.5 times the fish size horizontally and the full fish size vertically
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 the fish's tail as a triangle pointing to the left, making it look like a swimming fish
rect(this.x + FISH_SIZE * 0.5, this.y - FISH_SIZE * 0.3, FISH_SIZE * 0.7, FISH_SIZE * 0.2);- Draws a grey rectangle on the fish's right side to represent the missile launcher
this.vy += GRAVITY;- Each frame, adds the gravity constant to the vertical velocity, accelerating the fish downward
this.y += this.vy;- Updates the fish's y-position by adding its vertical velocity, moving it down when gravity is positive
this.y = constrain(this.y, FISH_SIZE / 2, height - FISH_SIZE / 2);- Forces the fish's y-position to stay within the canvas bounds, preventing it from moving beyond the top or bottom
if (this.y === FISH_SIZE / 2 || this.y === height - FISH_SIZE / 2) {- Checks if the fish has hit the top or bottom edge of the canvas—if so, the game ends
this.vy = JUMP_FORCE;- Sets the fish's vertical velocity to a negative value (JUMP_FORCE = -10) to make it move upward
jumpSound.amp(0.5, 0.1); setTimeout(() => jumpSound.amp(0, 0.1), 100);- Plays the jump sound by setting its amplitude to 0.5 over 0.1 seconds, then fades it out after 100 milliseconds
let missile = { x: this.x + FISH_SIZE * 1.2, y: this.y - FISH_SIZE * 0.2, ... };- Creates a new missile object at a position slightly ahead of and above the fish's launcher
missiles.push(missile);- Adds the newly created missile to the missiles array, making it active in the game
lastShotTime = millis();- Records the current time in milliseconds, used to enforce the SHOOT_COOLDOWN between shots
let fishRect = { x: this.x - FISH_SIZE * 0.75, y: this.y - FISH_SIZE / 2, width: FISH_SIZE * 1.5, height: FISH_SIZE };- Defines a bounding rectangle around the fish's body for accurate collision detection
if (checkRectCollision(fishRect, obstacleTopRect)) { return true; }- Tests whether the fish's bounding box overlaps with the top seaweed obstacle using rectangle collision detection
userStartAudio();- Initializes the web audio context, required by browsers before any sound can play