Students play a click-to-catch game, learn that mousePressed runs on its own and that touching means a small distance, fix a catch that scores for every miss, add lives with a game over and a restart key, and make a power-up ball.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 15 min · Modify 15 min · Make 20 min
Read the unit primer first. This is the Python Catch the turtle challenge in JavaScript, and it completes the course. Everything has been taught except events and joining text: mousePressed() and keyPressed() are functions p5 calls for you. Click coordinates are the sketch's mouseX and mouseY. The common slip is > where < belongs, so the bug task is a logic error with no red line: students must read the condition and play. The game needs the sketch to be clicked first for keys to work. This lesson marks "Challenge complete".
Close means a small distance. Play the buggy version and say what is happening before reading the code.
| What you will see | A question to ask | The fix |
|---|---|---|
| Every click scores, even misses. | Is the distance supposed to be bigger or smaller than 20 for a catch? | Use dist(...) < 20. |
| Pressing R does nothing. | Has the sketch been clicked first, and is lives zero? | Click the picture, play until GAME OVER, then press R. |
| Lives go below zero or the game keeps moving after GAME OVER. | Is the movement inside if (lives > 0)? | Wrap the loop in if (lives > 0) { ... } else { ... }. |
| The power-up looks the same as the others. | Where is its fill chosen? | Choose fill with an if on the property before drawing the circle. |
One possible solution for each task. Other correct programs are fine.
let balls = [];
let score = 0;
let lives = 3;
function setup() {
createCanvas(300, 200);
for (let i = 0; i < 4; i++) {
balls.push({ x: random(20, 280), y: random(-100, 0), speed: random(1, 2.5) });
}
}
function draw() {
background(20, 30, 60);
if (lives > 0) {
fill(255, 210, 0);
for (let ball of balls) {
circle(ball.x, ball.y, 24);
ball.y = ball.y + ball.speed;
if (ball.y > 210) {
lives = lives - 1;
restart(ball);
}
}
fill(255);
text("Score: " + score + " Lives: " + lives, 10, 20);
} else {
fill(255, 100, 100);
textSize(24);
text("GAME OVER", 80, 100);
textSize(12);
fill(255);
text("Score: " + score, 120, 125);
}
}
function restart(ball) {
ball.x = random(20, 280);
ball.y = -10;
}
function mousePressed() {
for (let ball of balls) {
if (dist(mouseX, mouseY, ball.x, ball.y) < 20) {
score = score + 1;
restart(ball);
}
}
}
let balls = [];
let score = 0;
let lives = 3;
function setup() {
createCanvas(300, 200);
for (let i = 0; i < 4; i++) {
balls.push({ x: random(20, 280), y: random(-100, 0), speed: random(1, 2.5) });
}
}
function draw() {
background(20, 30, 60);
if (lives > 0) {
fill(255, 210, 0);
for (let ball of balls) {
circle(ball.x, ball.y, 24);
ball.y = ball.y + ball.speed;
if (ball.y > 210) {
lives = lives - 1;
restart(ball);
}
}
fill(255);
text("Score: " + score + " Lives: " + lives, 10, 20);
} else {
fill(255, 100, 100);
textSize(24);
text("GAME OVER", 80, 100);
textSize(12);
fill(255);
text("Score: " + score + " Press R to play again", 60, 125);
}
}
function restart(ball) {
ball.x = random(20, 280);
ball.y = -10;
}
function mousePressed() {
for (let ball of balls) {
if (dist(mouseX, mouseY, ball.x, ball.y) < 20) {
score = score + 1;
restart(ball);
}
}
}
function keyPressed() {
if (key === "r" && lives === 0) {
score = 0;
lives = 3;
}
}
let balls = [];
let score = 0;
function setup() {
createCanvas(300, 200);
for (let i = 0; i < 4; i++) {
balls.push({ x: random(20, 280), y: random(-100, 0), speed: random(1, 2.5), gold: false });
}
balls.push({ x: 150, y: -50, speed: 3.5, gold: true });
}
function draw() {
background(20, 30, 60);
for (let ball of balls) {
if (ball.gold) {
fill(255, 255, 255);
} else {
fill(255, 210, 0);
}
circle(ball.x, ball.y, 24);
ball.y = ball.y + ball.speed;
if (ball.y > 210) {
restart(ball);
}
}
fill(255);
text("Score: " + score, 10, 20);
}
function restart(ball) {
ball.x = random(20, 280);
ball.y = -10;
}
function mousePressed() {
for (let ball of balls) {
if (dist(mouseX, mouseY, ball.x, ball.y) < 20) {
if (ball.gold) {
score = score + 5;
} else {
score = score + 1;
}
restart(ball);
}
}
}
Stretch: Make the balls get faster as the score rises, or add a high score kept in a variable.
Support: Give the lives code and ask only for the restart key, or give the gold ball and ask for a second power-up.
Why is the catch test dist(...) < 20 and not > 20?
Two things touch when the distance between them is small.
Optional next lesson: moving art with sin, cos and map.