Player class
The Player class manages the spaceship—its position, movement, collision detection, and score. Notice how the constructor() initializes all properties, update() handles movement logic, and display() handles drawing and animations. This pattern (constructor, update, display) is the standard way to organize game objects in p5.js.
🔬 This code chooses between touch and mouse input. What happens if you remove the if-statement and always use mouseX/mouseY instead? Try removing the touches.length check entirely—will touch still work?
// Determine target based on mouse or touch
let targetX, targetY;
if (touches.length > 0) {
targetX = touches[0].x;
targetY = touches[0].y;
} else {
targetX = mouseX;
targetY = mouseY;
}
🔬 These three vertices form a triangle. What happens if you change the second vertex to (-this.size / 2, -this.size / 2) and the third to (-this.size / 2, this.size / 2)? The spaceship will look more squared-off.
// Main body
beginShape();
vertex(this.size / 2, 0);
vertex(-this.size / 2, -this.size / 3);
vertex(-this.size / 2, this.size / 3);
endShape(CLOSE);
class Player {
constructor() {
this.x = width / 2;
this.y = height / 2;
this.size = 30;
this.speed = 5;
this.color = color(255, 100, 100);
this.score = 0;
}
update() {
// Determine target based on mouse or touch
let targetX, targetY;
if (touches.length > 0) {
targetX = touches[0].x;
targetY = touches[0].y;
} else {
targetX = mouseX;
targetY = mouseY;
}
// Move player towards target
this.x += (targetX - this.x) * 0.1;
this.y += (targetY - this.y) * 0.1;
// Keep player within bounds
this.x = constrain(this.x, 0, width);
this.y = constrain(this.y, 0, height);
}
display() {
push();
translate(this.x, this.y);
// Rotate towards movement direction for a spaceship feel
let angle = atan2(mouseY - this.y, mouseX - this.x);
rotate(angle);
// Draw the player ship (a triangle with a tail)
fill(this.color);
stroke(255);
strokeWeight(2);
// Main body
beginShape();
vertex(this.size / 2, 0);
vertex(-this.size / 2, -this.size / 3);
vertex(-this.size / 2, this.size / 3);
endShape(CLOSE);
// Tail (thruster effect)
fill(255, 150, 0); // Orange for thruster
noStroke();
let tailWidth = map(sin(frameCount * 0.2), -1, 1, 5, 15);
rect(-this.size, -tailWidth / 2, this.size / 2, tailWidth);
pop();
// Display score
fill(255);
noStroke();
textSize(24);
textAlign(RIGHT, TOP);
text(`Score: ${this.score}`, width - 20, 20);
}
// Check if player is colliding with a critter
collides(critter) {
let d = dist(this.x, this.y, critter.x, critter.y);
return d < this.size / 2 + critter.size / 2;
}
// Increase score and reset critter
collectCritter(critter) {
this.score += 10; // Award points
critter.reset(); // Make a new critter at a new location
}
}
Line-by-line explanation (14 lines)
🔧 Subcomponents:
if (touches.length > 0) {
Checks if the device is being touched; if so, use touch position; otherwise use mouse position
this.x += (targetX - this.x) * 0.1;
Moves the spaceship toward the target by 10% of the remaining distance each frame, creating smooth easing
return d < this.size / 2 + critter.size / 2;
Returns true if the distance between spaceship and critter centers is less than their combined radii
this.x = width / 2;- Positions the spaceship at the horizontal center of the canvas
this.y = height / 2;- Positions the spaceship at the vertical center of the canvas
this.size = 30;- Sets the spaceship's diameter to 30 pixels, used for drawing and collision detection
this.color = color(255, 100, 100);- Creates a reddish color (RGB: red=255, green=100, blue=100) for the spaceship body
if (touches.length > 0) {- Checks if any fingers are currently touching the screen; allows mobile gameplay without a mouse
this.x += (targetX - this.x) * 0.1;- Calculates the distance to the target, multiplies by 0.1 (10%), and moves that fraction toward it each frame—this creates smooth, eased motion instead of snapping instantly
this.x = constrain(this.x, 0, width);- Clamps the x position between 0 and canvas width, preventing the spaceship from drifting off-screen
let angle = atan2(mouseY - this.y, mouseX - this.x);- Calculates the angle from the spaceship to the mouse using atan2, so the spaceship visually points toward where you're aiming
rotate(angle);- Rotates the spaceship to point toward the mouse before drawing it
vertex(this.size / 2, 0);- Places the tip of the spaceship triangle at the right, scaled to half the size value
let tailWidth = map(sin(frameCount * 0.2), -1, 1, 5, 15);- Creates an animated thruster by mapping a sine wave (which oscillates -1 to 1) to a width between 5 and 15 pixels
let d = dist(this.x, this.y, critter.x, critter.y);- Calculates the Euclidean distance between the spaceship's center and the critter's center in pixels
return d < this.size / 2 + critter.size / 2;- Returns true if distance is less than the sum of both radii (spaceship radius + critter radius)—this is the standard circle collision test
this.score += 10;- Adds 10 points to the player's score whenever a critter is collected