the wimpy game

This sketch creates a dodge-and-collect game where players control a wimpy stick figure kid navigating a school hallway. The player must avoid falling bullies while collecting homework pages to increase their score, losing lives when hit by bullies and ending the game when all three lives are lost.

🧪 Try This!

Experiment with the code by making these changes:

  1. Double the bully spawn rate — Bullies will spawn twice as fast, making the game immediately harder from the start.
  2. Make homework worth more points — Collecting homework grants 10 points instead of 5, rewarding the player for dodging and collecting.
  3. Make the player move faster — The player will zip across the screen more easily, making dodge patterns feel more responsive.
  4. Give the player more lives — Start with 5 lives instead of 3, making the game easier and giving more chances to learn patterns.
  5. Make bullies much larger — Bullies will spawn with a larger size range, making them harder to dodge but easier to spot.
  6. Change the background color — The notebook paper changes from light blue to a different shade, altering the visual theme instantly.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a complete dodge-and-collect game featuring a nervous stick figure kid dodging falling bullies while grabbing homework pages. The visuals combine a paper-themed background with simple geometric shapes for the player, enemies, and collectibles, all animated smoothly down the screen. It uses fundamental game development techniques: game state management (start/playing/gameOver), object spawning with randomization, axis-aligned bounding box collision detection, and difficulty progression that accelerates enemy spawn rates over time.

The code is organized around a central game loop that handles player input, spawns new obstacles and collectibles, updates their positions, checks collisions, and renders everything each frame. By studying it you will learn how to structure a complete mini-game with multiple screens, manage lists of moving objects, detect when rectangles overlap, and gradually increase difficulty—skills that transfer directly to any game-making project.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-window canvas and initializes the player object as a stick figure at the bottom center. The gameState variable is set to 'start', showing a title screen with controls and waiting for the player to press ENTER.
  2. Once the player presses ENTER, gameState changes to 'playing' and the main game loop begins: handlePlayerMovement() reads arrow keys and WASD to move the player left/right/up/down while keeping them inside the hallway bounds.
  3. On every frame, spawnHazards() and spawnGoodies() use frame counters to create new bullies and homework pages at random x-positions above the screen at timed intervals. The spawn intervals decrease slightly each time, making the game progressively harder.
  4. updateHazards() and updateGoodies() move all falling objects down the screen by their speed values, removing them when they fall off the bottom.
  5. checkCollisions() uses the rectOverlap() function to test if the player's rectangular hitbox overlaps with any bully or homework, subtracting a life on bully contact or adding 5 points on homework contact.
  6. The entire screen is redrawn each frame with drawPaperBackground() creating a notebook aesthetic, drawGame() rendering all game objects and UI, and when lives reach zero, drawGameOverScreen() overlays a dark panel with the final score and restart instructions.

🎓 Concepts You'll Learn

Game state managementCollision detection (AABB)Object spawning and poolingInput handlingDifficulty progressionKinematic animation

📝 Code Breakdown

setup()

setup() runs once when the sketch starts and is the perfect place to initialize all your game variables and canvas configuration. By storing the player data in an object, we can easily add new properties like direction or score later.

function setup() {
  createCanvas(windowWidth, windowHeight);

  player = {
    x: width / 2,
    y: height - 120,
    w: 40,
    h: 80,
    speed: 7
  };

  resetGame(false); // set initial values but stay on start screen
}
Line-by-line explanation (6 lines)
createCanvas(windowWidth, windowHeight);
Creates a canvas that fills the entire browser window, allowing the game to be responsive to different screen sizes.
player = {
Initializes the player as an object with x/y position, width/height dimensions (the hitbox), and a movement speed value.
x: width / 2,
Places the player horizontally at the center of the canvas.
y: height - 120,
Places the player near the bottom of the screen where the hallway starts, leaving room for the UI above.
speed: 7
Defines how many pixels the player moves per frame when the player presses movement keys.
resetGame(false);
Calls resetGame with false so it initializes game variables (score, lives, enemies, collectibles) but keeps gameState as 'start' to display the title screen.

resetGame(startImmediately)

resetGame() demonstrates the pattern of resetting all game state in one place—essential for restarting without memory leaks or stale data. The startImmediately parameter is a clean way to reuse the same reset logic for both initialization and restart.

function resetGame(startImmediately = true) {
  hazards = [];
  goodies = [];
  score = 0;
  lives = 3;
  hazardSpawnInterval = 55;
  goodieSpawnInterval = 110;
  lastHazardSpawnFrame = frameCount;
  lastGoodieSpawnFrame = frameCount;
  player.x = width / 2;
  player.y = height - 120;

  if (startImmediately) {
    gameState = 'playing';
  }
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

calculation Clear enemy and item arrays hazards = []; goodies = [];

Empties both arrays so no leftover bullies or homework from the previous game remain

calculation Reset score and lives score = 0; lives = 3;

Returns the player's score to zero and gives them three fresh lives to start the game

calculation Reset difficulty settings hazardSpawnInterval = 55; goodieSpawnInterval = 110;

Resets spawn intervals to their original values so difficulty progression starts fresh

conditional Conditional game start if (startImmediately) { gameState = 'playing'; }

Only transitions to 'playing' state if the parameter is true; called with false from setup() to show the start screen first

function resetGame(startImmediately = true) {
Defines a function with a parameter that defaults to true, allowing callers to reset without arguments or control the behavior with false.
hazards = [];
Clears the hazards array by assigning it a new empty array, removing all bullies from the screen.
goodies = [];
Clears the goodies array, removing all homework pages from the screen.
score = 0;
Resets the score counter to zero for a fresh game.
lives = 3;
Resets lives to 3, giving the player three chances to avoid bullies.
hazardSpawnInterval = 55;
Resets the spawn interval for bullies to its initial value, undoing any difficulty increases from the previous game.
goodieSpawnInterval = 110;
Resets the spawn interval for homework to its initial value.
lastHazardSpawnFrame = frameCount;
Records the current frame number so the spawn timer for bullies starts fresh.
lastGoodieSpawnFrame = frameCount;
Records the current frame number so the spawn timer for homework starts fresh.
if (startImmediately) {
Checks the parameter: if true, transition to 'playing'; if false, stay on the start screen.
gameState = 'playing';
Changes the game state, which tells the draw() function to show gameplay instead of the start screen.

draw()

The draw() function is p5.js's main loop, called 60 times per second. Its job is to route control to the correct screen based on gameState. This state-machine pattern is the foundation of games: different screens handle different logic.

🔬 This chain of if/else statements routes to different screens. What happens if you swap the order of the conditions—move the 'gameOver' check to the top?

  if (gameState === 'start') {
    drawStartScreen();
  } else if (gameState === 'playing') {
    updateGame();
    drawGame();
  } else if (gameState === 'gameOver') {
    drawGame();
    drawGameOverScreen();
  }
function draw() {
  if (gameState === 'start') {
    drawStartScreen();
  } else if (gameState === 'playing') {
    updateGame();
    drawGame();
  } else if (gameState === 'gameOver') {
    drawGame();
    drawGameOverScreen();
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

conditional Start screen state if (gameState === 'start') { drawStartScreen(); }

When the game first loads, display the title screen with instructions

conditional Active gameplay state } else if (gameState === 'playing') { updateGame(); drawGame(); }

During active gameplay, update all game logic then render the scene

conditional Game over screen state } else if (gameState === 'gameOver') { drawGame(); drawGameOverScreen(); }

Show the frozen game in the background with a game-over overlay on top

if (gameState === 'start') {
Checks if the current game state is 'start', indicating the game hasn't begun yet.
drawStartScreen();
Calls the function that renders the title screen with game name, instructions, and the 'Press ENTER to Start' message.
} else if (gameState === 'playing') {
If the state is 'playing', the game is actively running.
updateGame();
Calls the function that updates all game logic: player movement, spawning enemies, checking collisions, and updating object positions.
drawGame();
Calls the function that renders all game elements to the canvas after logic has been updated.
} else if (gameState === 'gameOver') {
If the state is 'gameOver', the player has lost all their lives.
drawGame();
Still renders the game world in the background so the player can see their final position.
drawGameOverScreen();
Overlays the game-over screen on top with the final score and restart instructions.

updateGame()

updateGame() is a coordinator function that calls all the smaller functions that make the game work. This organization keeps draw() clean and makes it easy to understand the game loop at a glance: input → spawning → movement → collision checks.

function updateGame() {
  handlePlayerMovement();
  spawnHazards();
  spawnGoodies();
  updateHazards();
  updateGoodies();
  checkCollisions();
}
Line-by-line explanation (6 lines)
handlePlayerMovement();
Processes arrow key and WASD input, moving the player and keeping them inside the hallway bounds.
spawnHazards();
Checks if enough frames have passed and randomly creates new bullies at the top of the screen.
spawnGoodies();
Checks if enough frames have passed and randomly creates new homework pages at the top of the screen.
updateHazards();
Moves all bullies downward by their speed values and removes any that have fallen off the bottom.
updateGoodies();
Moves all homework pages downward and removes any that have fallen off the bottom.
checkCollisions();
Tests if the player has touched any bullies (lose a life) or homework (gain points).

handlePlayerMovement()

This function demonstrates keyIsDown(), which tests if a key is currently held down (perfect for smooth, continuous movement). The constrain() function is a safety net that prevents the player from leaving the playable area.

🔬 These two if-statements check for left and right input independently—what happens if you change the second one to 'else if' so the player can't move left and right simultaneously?

  if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) { // LEFT or 'A'
    player.x -= player.speed;
  }
  if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) { // RIGHT or 'D'
    player.x += player.speed;
  }
function handlePlayerMovement() {
  // Horizontal movement
  if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) { // LEFT or 'A'
    player.x -= player.speed;
  }
  if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) { // RIGHT or 'D'
    player.x += player.speed;
  }

  // Vertical movement (up/down the hallway)
  if (keyIsDown(UP_ARROW) || keyIsDown(87)) { // UP or 'W'
    player.y -= player.speed;
  }
  if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) { // DOWN or 'S'
    player.y += player.speed;
  }

  // Keep inside hallway area
  const topHall = 100;
  const bottomHall = height - 80;
  player.x = constrain(player.x, player.w / 2 + 20, width - player.w / 2 - 20);
  player.y = constrain(player.y, topHall + player.h / 2, bottomHall - player.h / 2);
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

conditional Horizontal movement input if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) { // LEFT or 'A' player.x -= player.speed; } if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) { // RIGHT or 'D' player.x += player.speed; }

Checks for left/A or right/D keys and moves the player left or right by subtracting or adding the speed value

conditional Vertical movement input if (keyIsDown(UP_ARROW) || keyIsDown(87)) { // UP or 'W' player.y -= player.speed; } if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) { // DOWN or 'S' player.y += player.speed; }

Checks for up/W or down/S keys and moves the player up or down the hallway

calculation Boundary clamping player.x = constrain(player.x, player.w / 2 + 20, width - player.w / 2 - 20); player.y = constrain(player.y, topHall + player.h / 2, bottomHall - player.h / 2);

Uses constrain() to keep the player's position within safe bounds so they can't move off-screen

if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) { // LEFT or 'A'
Tests if either the LEFT_ARROW key or the 'A' key (ASCII 65) is currently held down. The || means either condition satisfies this.
player.x -= player.speed;
Moves the player left by subtracting speed from their x position. The -= operator is shorthand for player.x = player.x - player.speed.
if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) { // RIGHT or 'D'
Tests if RIGHT_ARROW or 'D' (ASCII 68) is held down.
player.x += player.speed;
Moves the player right by adding speed to their x position.
if (keyIsDown(UP_ARROW) || keyIsDown(87)) { // UP or 'W'
Tests if UP_ARROW or 'W' (ASCII 87) is held down to move up the hallway.
player.y -= player.speed;
Moves the player up by subtracting speed from their y position (remember: in p5.js, y decreases going upward).
if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) { // DOWN or 'S'
Tests if DOWN_ARROW or 'S' (ASCII 83) is held down.
player.y += player.speed;
Moves the player down by adding speed to their y position.
const topHall = 100;
Defines a constant for the topmost safe position in the hallway, preventing the player from moving into the title area.
const bottomHall = height - 80;
Defines a constant for the bottommost safe position, preventing the player from moving off the bottom of the screen.
player.x = constrain(player.x, player.w / 2 + 20, width - player.w / 2 - 20);
Clamps the player's x position between a left and right boundary, using the player's width to prevent the sprite from extending beyond the screen edges.
player.y = constrain(player.y, topHall + player.h / 2, bottomHall - player.h / 2);
Clamps the player's y position between the top and bottom hallway boundaries, accounting for the player's height.

spawnHazards()

spawnHazards() demonstrates frame-based spawning: using frame count differences to time events instead of milliseconds. This gives you precise control over spawn rates and makes difficulty scaling simple by tweaking a single variable.

🔬 This object has five properties: x, y, w, h, and speed. What happens if you change the y: -h line to y: 0 to start bullies on-screen instead of above it?

    hazards.push({
      x: random(w / 2 + 20, width - w / 2 - 20),
      y: -h, // start above screen
      w: w,
      h: h,
      speed: random(3, 6)
    });
function spawnHazards() {
  if (frameCount - lastHazardSpawnFrame > hazardSpawnInterval) {
    const w = random(40, 60);
    const h = random(50, 90);
    hazards.push({
      x: random(w / 2 + 20, width - w / 2 - 20),
      y: -h, // start above screen
      w: w,
      h: h,
      speed: random(3, 6)
    });
    lastHazardSpawnFrame = frameCount;

    // Make game harder over time
    if (hazardSpawnInterval > 25) {
      hazardSpawnInterval -= 0.05;
    }
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

conditional Spawn interval timer if (frameCount - lastHazardSpawnFrame > hazardSpawnInterval) {

Checks if enough frames have passed since the last bully spawn—prevents spawning every single frame

calculation Random bully dimensions const w = random(40, 60); const h = random(50, 90);

Generates random width and height for each bully so they aren't all identical

calculation Create bully object hazards.push({ x: random(w / 2 + 20, width - w / 2 - 20), y: -h, // start above screen w: w, h: h, speed: random(3, 6) });

Creates a new bully object with randomized position, size, and speed, then adds it to the hazards array

conditional Difficulty progression if (hazardSpawnInterval > 25) { hazardSpawnInterval -= 0.05; }

Gradually decreases the spawn interval so bullies appear more frequently as the game progresses, making it harder

if (frameCount - lastHazardSpawnFrame > hazardSpawnInterval) {
Compares the number of frames elapsed since the last spawn against the spawn interval. If more frames have passed than the interval allows, it's time to spawn a new bully.
const w = random(40, 60);
Generates a random width between 40 and 60 pixels for this bully, making each one slightly different.
const h = random(50, 90);
Generates a random height between 50 and 90 pixels, further varying each bully's appearance.
hazards.push({
Uses the push() method to add a new object to the hazards array.
x: random(w / 2 + 20, width - w / 2 - 20),
Sets a random x position across the width of the hallway, accounting for the bully's width so it doesn't spawn partially off-screen.
y: -h, // start above screen
Places the bully above the visible canvas (negative y) so it appears to fall from the top.
speed: random(3, 6)
Assigns a random falling speed between 3 and 6 pixels per frame, making some bullies fall faster than others.
lastHazardSpawnFrame = frameCount;
Records the current frame number as the spawn time, resetting the countdown for the next bully.
if (hazardSpawnInterval > 25) {
Only decreases the spawn interval if it's still above 25, preventing bullies from spawning infinitely fast.
hazardSpawnInterval -= 0.05;
Reduces the spawn interval by a tiny amount, making bullies appear more frequently over time and increasing difficulty.

spawnGoodies()

spawnGoodies() mirrors spawnHazards() but for collectibles. Notice the speed range (2-4) is slower than hazards (3-6), making homework easier to catch and giving players moments of relief.

function spawnGoodies() {
  if (frameCount - lastGoodieSpawnFrame > goodieSpawnInterval) {
    const size = 30;
    goodies.push({
      x: random(size / 2 + 20, width - size / 2 - 20),
      y: -size,
      w: size,
      h: size,
      speed: random(2, 4)
    });
    lastGoodieSpawnFrame = frameCount;

    if (goodieSpawnInterval > 70) {
      goodieSpawnInterval -= 0.1;
    }
  }
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

conditional Goodie spawn interval timer if (frameCount - lastGoodieSpawnFrame > goodieSpawnInterval) {

Checks if enough frames have passed to spawn the next homework page

calculation Create goodie object goodies.push({ x: random(size / 2 + 20, width - size / 2 - 20), y: -size, w: size, h: size, speed: random(2, 4) });

Creates a homework page object with randomized x-position, fixed size, and random falling speed

if (frameCount - lastGoodieSpawnFrame > goodieSpawnInterval) {
Checks if enough frames have elapsed to spawn another homework page.
const size = 30;
Defines a fixed size of 30 pixels for all homework pages, making them uniform and predictable to collect.
goodies.push({
Adds a new homework page object to the goodies array.
x: random(size / 2 + 20, width - size / 2 - 20),
Places the homework at a random horizontal position, accounting for its size so it stays on-screen.
y: -size,
Starts the homework above the visible canvas so it falls from the top.
speed: random(2, 4)
Assigns a random falling speed slightly slower than bullies, making homework easier to catch.
lastGoodieSpawnFrame = frameCount;
Records the current frame number to reset the spawn timer for the next homework page.
if (goodieSpawnInterval > 70) {
Checks if the goodie spawn interval can still decrease (stops decreasing at 70 to prevent too many goodies).
goodieSpawnInterval -= 0.1;
Slowly decreases the spawn interval, making homework pages appear more frequently over time.

updateHazards()

This function demonstrates two critical patterns: reverse-loop iteration (essential when modifying arrays) and off-screen object culling (removing items you can no longer see to save memory). The splice() method removes elements without leaving gaps.

🔬 This loop counts backward (i--) instead of forward. What happens if you change it to count forward with i++ starting from 0?

  for (let i = hazards.length - 1; i >= 0; i--) {
    let h = hazards[i];
    h.y += h.speed;
function updateHazards() {
  for (let i = hazards.length - 1; i >= 0; i--) {
    let h = hazards[i];
    h.y += h.speed;

    // Remove off-screen bullies
    if (h.y - h.h / 2 > height) {
      hazards.splice(i, 1);
    }
  }
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

for-loop Reverse iteration loop for (let i = hazards.length - 1; i >= 0; i--) {

Loops through hazards backwards so removing items doesn't skip any elements

calculation Move bully down h.y += h.speed;

Increases each bully's y position by its speed, making it fall downward

conditional Remove off-screen bullies if (h.y - h.h / 2 > height) { hazards.splice(i, 1); }

Deletes bullies that have fallen completely off the bottom of the screen to free up memory

for (let i = hazards.length - 1; i >= 0; i--) {
Starts at the last element of the hazards array (length - 1) and counts backward to 0. Looping backward is critical when removing items: if you loop forward and remove element 5, element 6 becomes element 5, and you'd skip checking it.
let h = hazards[i];
Assigns the current bully object to the variable h, making the code more readable.
h.y += h.speed;
Moves the bully downward by adding its speed to its y position. In p5.js, positive y is downward.
if (h.y - h.h / 2 > height) {
Checks if the bully's bottom edge (center y minus half height) has passed the bottom of the canvas. If true, it's completely off-screen.
hazards.splice(i, 1);
Removes the bully at index i from the hazards array. splice(i, 1) means 'starting at i, remove 1 element'.

updateGoodies()

updateGoodies() is nearly identical to updateHazards()—an example of how the same pattern applies to different object types. In a larger game, you might refactor this into a single function that works on any array.

function updateGoodies() {
  for (let i = goodies.length - 1; i >= 0; i--) {
    let g = goodies[i];
    g.y += g.speed;

    // Remove off-screen homework
    if (g.y - g.h / 2 > height) {
      goodies.splice(i, 1);
    }
  }
}
Line-by-line explanation (5 lines)
for (let i = goodies.length - 1; i >= 0; i--) {
Loops through the goodies array backward, same pattern as updateHazards().
let g = goodies[i];
Assigns the current homework object to the variable g.
g.y += g.speed;
Moves the homework downward by its speed value.
if (g.y - g.h / 2 > height) {
Checks if the homework has fallen off the bottom of the screen.
goodies.splice(i, 1);
Removes the homework from the goodies array.

checkCollisions()

checkCollisions() demonstrates using a helper function (rectOverlap) to simplify collision tests. This separates concerns: the collision helper handles the math, and checkCollisions() handles the game logic.

🔬 Right now, hitting a bully removes it and costs a life. What happens if you comment out the hazards.splice(i, 1) line so bullies stay on-screen after hitting you?

  for (let i = hazards.length - 1; i >= 0; i--) {
    let h = hazards[i];
    if (rectOverlap(player, h)) {
      hazards.splice(i, 1);
      lives--;
function checkCollisions() {
  // Player vs bullies (hazards)
  for (let i = hazards.length - 1; i >= 0; i--) {
    let h = hazards[i];
    if (rectOverlap(player, h)) {
      hazards.splice(i, 1);
      lives--;
      if (lives <= 0) {
        gameState = 'gameOver';
      }
    }
  }

  // Player vs homework (goodies)
  for (let i = goodies.length - 1; i >= 0; i--) {
    let g = goodies[i];
    if (rectOverlap(player, g)) {
      score += 5;
      goodies.splice(i, 1);
    }
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

for-loop Bully collision detection for (let i = hazards.length - 1; i >= 0; i--) { let h = hazards[i]; if (rectOverlap(player, h)) { hazards.splice(i, 1); lives--; if (lives <= 0) { gameState = 'gameOver'; } } }

Tests each bully against the player; if they touch, remove the bully, lose a life, and end the game if lives run out

for-loop Homework collision detection for (let i = goodies.length - 1; i >= 0; i--) { let g = goodies[i]; if (rectOverlap(player, g)) { score += 5; goodies.splice(i, 1); } }

Tests each homework against the player; if they touch, add 5 to the score and remove the homework

for (let i = hazards.length - 1; i >= 0; i--) {
Loops backward through all bullies.
if (rectOverlap(player, h)) {
Calls rectOverlap() to test if the player's hitbox overlaps with this bully's hitbox.
hazards.splice(i, 1);
Removes the bully from the array so it doesn't hit the player multiple times.
lives--;
Decreases the lives count by 1 after being hit.
if (lives <= 0) {
Checks if lives have reached zero or below.
gameState = 'gameOver';
Transitions to the game-over screen when the player runs out of lives.
for (let i = goodies.length - 1; i >= 0; i--) {
Loops backward through all homework pages.
if (rectOverlap(player, g)) {
Tests if the player overlaps with this homework.
score += 5;
Adds 5 points to the score when homework is collected.
goodies.splice(i, 1);
Removes the homework from the array so it can't be collected twice.

rectOverlap(a, b)

rectOverlap() implements Axis-Aligned Bounding Box (AABB) collision detection—the simplest and fastest method for rectangular objects. It returns true only when none of the four separation conditions are true, meaning the rectangles must overlap. This logic powers most 2D games.

// Axis-aligned bounding-box collision (rects centered on x,y)
function rectOverlap(a, b) {
  return !(
    a.x + a.w / 2 < b.x - b.w / 2 ||
    a.x - a.w / 2 > b.x + b.w / 2 ||
    a.y + a.h / 2 < b.y - b.h / 2 ||
    a.y - a.h / 2 > b.y + b.h / 2
  );
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Horizontal separation test a.x + a.w / 2 < b.x - b.w / 2 ||

Checks if object a's right edge is to the left of object b's left edge (they're separated horizontally)

calculation Left-right separation test a.x - a.w / 2 > b.x + b.w / 2 ||

Checks if object a's left edge is to the right of object b's right edge (they're separated horizontally)

calculation Vertical separation test a.y + a.h / 2 < b.y - b.h / 2 ||

Checks if object a's bottom edge is above object b's top edge (they're separated vertically)

calculation Bottom-top separation test a.y - a.h / 2 > b.y + b.h / 2

Checks if object a's top edge is below object b's bottom edge (they're separated vertically)

function rectOverlap(a, b) {
Defines a helper function that takes two rectangle objects (a and b) and returns true if they overlap.
return !(
Returns the opposite of what follows: if the rectangles are NOT separated, they must be overlapping. The ! operator inverts the boolean result.
a.x + a.w / 2 < b.x - b.w / 2 ||
Checks if a's right edge is left of b's left edge. If true, they're separated on the x-axis and can't be overlapping.
a.x - a.w / 2 > b.x + b.w / 2 ||
Checks if a's left edge is right of b's right edge. Another way they could be separated horizontally.
a.y + a.h / 2 < b.y - b.h / 2 ||
Checks if a's bottom edge is above b's top edge. If true, they're separated vertically.
a.y - a.h / 2 > b.y + b.h / 2
Checks if a's top edge is below b's bottom edge. Another way they could be separated vertically.

drawPaperBackground()

drawPaperBackground() demonstrates how simple loops and lines create visual theme and mood. The notebook aesthetic makes the game feel cohesive with its 'wimpy kid in school' concept.

🔬 This loop draws lines every 40 pixels. What happens if you change 40 to 20 to make the lines twice as close together, creating smaller spacing?

  for (let y = 80; y < height; y += 40) {
    line(0, y, width, y);
  }
function drawPaperBackground() {
  // Light paper color
  background(245, 245, 255);

  // Horizontal "notebook" lines
  stroke(210, 220, 250);
  strokeWeight(1);
  for (let y = 80; y < height; y += 40) {
    line(0, y, width, y);
  }

  // Vertical red margin line
  stroke(255, 80, 80);
  line(80, 0, 80, height);
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

calculation Paper background color background(245, 245, 255);

Clears the canvas with a light off-white color that looks like notebook paper

for-loop Draw horizontal ruled lines for (let y = 80; y < height; y += 40) { line(0, y, width, y); }

Draws evenly-spaced horizontal lines across the canvas to simulate ruled notebook paper

calculation Draw vertical margin line stroke(255, 80, 80); line(80, 0, 80, height);

Draws a red vertical line on the left side to represent a notebook margin, adding visual theme

background(245, 245, 255);
Fills the entire canvas with a light blue-tinted white color (RGB values 245, 245, 255), creating the paper effect. This also clears any previous drawing from the last frame.
stroke(210, 220, 250);
Sets the line color to a light blue, matching the color of lines on real notebook paper.
strokeWeight(1);
Sets the line thickness to 1 pixel, keeping the ruled lines subtle and realistic.
for (let y = 80; y < height; y += 40) {
Loops from y = 80 to the bottom of the canvas, stepping by 40 pixels each iteration. This creates evenly-spaced lines 40 pixels apart.
line(0, y, width, y);
Draws a horizontal line from the left edge (x = 0) to the right edge (x = width) at the current y position.
stroke(255, 80, 80);
Changes the stroke color to red (RGB 255, 80, 80).
line(80, 0, 80, height);
Draws a vertical red line at x = 80 from the top to the bottom of the screen, simulating a notebook's left margin where you can't write.

drawPlayer()

drawPlayer() is a complete lesson in drawing custom shapes: lines for stick limbs, ellipses for the head, and arcs for hair and expression. Each coordinate is relative to player.x and player.y, so the entire figure moves when the player moves.

function drawPlayer() {
  rectMode(CENTER);
  ellipseMode(CENTER);
  noStroke();

  // Body hitbox (invisible)
  // rect(player.x, player.y, player.w, player.h);

  // Legs & body (stick figure)
  stroke(0);
  strokeWeight(3);

  // Body
  line(player.x, player.y - 10, player.x, player.y + 25);

  // Arms
  line(player.x, player.y + 5, player.x - 15, player.y + 15);
  line(player.x, player.y + 5, player.x + 15, player.y + 15);

  // Legs
  line(player.x, player.y + 25, player.x - 12, player.y + 45);
  line(player.x, player.y + 25, player.x + 12, player.y + 45);

  // Head
  noStroke();
  fill(255, 230, 200);
  ellipse(player.x, player.y - 25, 26, 26);

  // Hair / outline
  noFill();
  stroke(0);
  strokeWeight(2);
  arc(player.x, player.y - 27, 26, 26, PI, 0);

  // Eyes
  noStroke();
  fill(0);
  ellipse(player.x - 4, player.y - 27, 3, 3);
  ellipse(player.x + 4, player.y - 27, 3, 3);

  // Mouth (worried)
  noFill();
  stroke(0);
  strokeWeight(1.5);
  arc(player.x, player.y - 18, 10, 6, PI, 0);
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

calculation Set drawing modes rectMode(CENTER); ellipseMode(CENTER); noStroke();

Configures all shapes to be drawn from their center point (not top-left), making positioning easier

calculation Draw stick figure body stroke(0); strokeWeight(3); line(player.x, player.y - 10, player.x, player.y + 25); line(player.x, player.y + 5, player.x - 15, player.y + 15); line(player.x, player.y + 5, player.x + 15, player.y + 15); line(player.x, player.y + 25, player.x - 12, player.y + 45); line(player.x, player.y + 25, player.x + 12, player.y + 45);

Draws five line segments forming a stick figure: vertical body, two arms, and two legs

calculation Draw head and facial features fill(255, 230, 200); ellipse(player.x, player.y - 25, 26, 26); arc(player.x, player.y - 27, 26, 26, PI, 0); ellipse(player.x - 4, player.y - 27, 3, 3); ellipse(player.x + 4, player.y - 27, 3, 3); arc(player.x, player.y - 18, 10, 6, PI, 0);

Draws a peach-colored circular head with an arc for hair on top, two small black eyes, and a worried mouth arc

rectMode(CENTER);
Sets rectangle drawing mode so all rectangles are positioned from their center point instead of top-left. This makes alignment easier.
ellipseMode(CENTER);
Sets ellipse drawing mode so circles and ellipses are also drawn from their center.
noStroke();
Disables stroke for shapes about to be drawn (though this is immediately overridden by the next stroke() call).
stroke(0);
Sets the stroke (outline) color to black (0 = pure black in grayscale).
strokeWeight(3);
Sets the stroke thickness to 3 pixels, making the stick figure's lines bold and visible.
line(player.x, player.y - 10, player.x, player.y + 25);
Draws a vertical line for the body, starting above the center and extending downward.
line(player.x, player.y + 5, player.x - 15, player.y + 15);
Draws the left arm, angling from the upper body downward and to the left.
line(player.x, player.y + 5, player.x + 15, player.y + 15);
Draws the right arm, angling from the upper body downward and to the right.
line(player.x, player.y + 25, player.x - 12, player.y + 45);
Draws the left leg, extending from the body's base downward and to the left.
line(player.x, player.y + 25, player.x + 12, player.y + 45);
Draws the right leg, extending from the body's base downward and to the right.
fill(255, 230, 200);
Sets the fill color to a peach/skin tone (light tan) for the head.
ellipse(player.x, player.y - 25, 26, 26);
Draws a circular head (26x26 pixels) positioned above the stick body.
arc(player.x, player.y - 27, 26, 26, PI, 0);
Draws a half-circle (arc from PI to 0 radians) on top of the head to represent hair.
ellipse(player.x - 4, player.y - 27, 3, 3);
Draws a small black circle for the left eye, positioned slightly to the left of the head's center.
ellipse(player.x + 4, player.y - 27, 3, 3);
Draws a small black circle for the right eye, positioned slightly to the right.
arc(player.x, player.y - 18, 10, 6, PI, 0);
Draws an arc below the eyes to represent a worried mouth—a downward-facing arc that matches the character's wimpy personality.

drawGame()

drawGame() is a comprehensive rendering function that layers multiple elements: background, title, enemies, collectibles, and player. It uses push/pop to manage coordinate transformations, ensuring rotated homework doesn't affect other objects.

🔬 The rotate(radians(-10)) tilts homework pages by -10 degrees. What happens if you change -10 to 0 to remove the tilt, or to 45 for a dramatic angle?

    push();
    translate(g.x, g.y);
    rotate(radians(-10));
function drawGame() {
  drawPaperBackground();

  // Draw hallway title at top
  noStroke();
  fill(50, 60, 120);
  textAlign(LEFT, TOP);
  textSize(20);
  text('Wimpy Kid School Run', 90, 10);

  // Draw bullies
  rectMode(CENTER);
  for (let h of hazards) {
    // bulky rectangle "bully"
    fill(200, 70, 70);
    rect(h.x, h.y, h.w, h.h, 6);
    // simple angry face
    fill(255, 230, 200);
    rect(h.x, h.y - h.h * 0.15, h.w * 0.5, h.h * 0.3, 4);
  }

  // Draw homework pages
  for (let g of goodies) {
    push();
    translate(g.x, g.y);
    rotate(radians(-10));
    fill(255);
    stroke(50, 80, 150);
    strokeWeight(1.5);
    rectMode(CENTER);
    rect(0, 0, g.w, g.h, 4);

    // lines on the page
    stroke(200, 0, 0);
    line(-g.w / 2 + 4, -4, g.w / 2 - 4, -4);
    stroke(0, 80, 200);
    line(-g.w / 2 + 4, 2, g.w / 2 - 4, 2);
    pop();
  }

  // Draw player last so it's on top
  drawPlayer();

  // UI: Score and Lives
  fill(0);
  noStroke();
  textSize(20);
  textAlign(LEFT, TOP);
  text('Score: ' + score, 90, 40);

  textAlign(RIGHT, TOP);
  text('Lives: ' + lives, width - 20, 20);
}
Line-by-line explanation (27 lines)

🔧 Subcomponents:

calculation Draw paper background and title drawPaperBackground(); text('Wimpy Kid School Run', 90, 10);

Calls the background function and renders the title at the top of the screen

for-loop Draw all bullies for (let h of hazards) { fill(200, 70, 70); rect(h.x, h.y, h.w, h.h, 6); fill(255, 230, 200); rect(h.x, h.y - h.h * 0.15, h.w * 0.5, h.h * 0.3, 4); }

Loops through all bullies and draws each as a red rectangle with a peach-colored face on top

for-loop Draw all homework pages for (let g of goodies) { push(); translate(g.x, g.y); rotate(radians(-10)); fill(255); stroke(50, 80, 150); strokeWeight(1.5); rectMode(CENTER); rect(0, 0, g.w, g.h, 4); stroke(200, 0, 0); line(-g.w / 2 + 4, -4, g.w / 2 - 4, -4); stroke(0, 80, 200); line(-g.w / 2 + 4, 2, g.w / 2 - 4, 2); pop(); }

Loops through all homework pages and draws each rotated by -10 degrees with red and blue lines to look like written pages

calculation Draw score and lives UI text('Score: ' + score, 90, 40); text('Lives: ' + lives, width - 20, 20);

Renders the current score and remaining lives in the top corners of the screen

drawPaperBackground();
Calls drawPaperBackground() to fill the canvas with the paper look and ruled lines.
noStroke();
Disables strokes so text will be drawn without outlines.
fill(50, 60, 120);
Sets text color to dark blue.
textAlign(LEFT, TOP);
Aligns text from the top-left corner of the text bounding box.
textSize(20);
Sets font size to 20 pixels.
text('Wimpy Kid School Run', 90, 10);
Renders the title text at coordinates (90, 10), below the red margin line.
for (let h of hazards) {
Loops through each bully object in the hazards array using the shorthand for...of syntax.
fill(200, 70, 70);
Sets the fill color to red-ish for the bully's body.
rect(h.x, h.y, h.w, h.h, 6);
Draws a red rectangle at the bully's position with its width and height. The 6 rounds the corners slightly.
fill(255, 230, 200);
Changes the fill to peach tone for the face.
rect(h.x, h.y - h.h * 0.15, h.w * 0.5, h.h * 0.3, 4);
Draws the face as a smaller peach rectangle positioned above the body center.
for (let g of goodies) {
Loops through each homework object in the goodies array.
push();
Saves the current transformation state (translation, rotation) so the rotation only applies to this homework page.
translate(g.x, g.y);
Moves the origin to the homework's position, so all subsequent drawing is relative to this point.
rotate(radians(-10));
Rotates the coordinate system by -10 degrees, making the homework appear tilted on the page.
fill(255);
Sets the fill color to white for the paper.
stroke(50, 80, 150);
Sets the stroke color to dark blue for the page border.
rect(0, 0, g.w, g.h, 4);
Draws the homework page as a white rectangle (now rotated) at the origin (0,0 after translation).
stroke(200, 0, 0);
Changes stroke to red for the first line on the paper.
line(-g.w / 2 + 4, -4, g.w / 2 - 4, -4);
Draws a red line near the top of the homework page, slightly inset from the edges.
stroke(0, 80, 200);
Changes stroke to blue for the second line.
line(-g.w / 2 + 4, 2, g.w / 2 - 4, 2);
Draws a blue line lower on the page, creating the impression of written homework.
pop();
Restores the previous transformation state, undoing the translation and rotation so the next homework or object is drawn normally.
drawPlayer();
Calls drawPlayer() to render the stick figure on top of everything else.
text('Score: ' + score, 90, 40);
Renders the current score below the title, using string concatenation to show the number.
textAlign(RIGHT, TOP);
Changes text alignment to right-justify.
text('Lives: ' + lives, width - 20, 20);
Renders lives in the top-right corner, 20 pixels from the right edge.

drawStartScreen()

drawStartScreen() demonstrates text-centered UI design using textAlign(CENTER, CENTER) and positioning text at relative positions like width / 2 and height / 2. This makes the screen responsive to different window sizes.

function drawStartScreen() {
  drawPaperBackground();

  fill(0);
  textAlign(CENTER, CENTER);

  textSize(46);
  text('Wimpy Kid School Run', width / 2, height / 2 - 100);

  textSize(22);
  text('You are a scrawny kid in a busy school hallway.', width / 2, height / 2 - 40);
  text('Dodge the bullies and grab the homework pages!', width / 2, height / 2 - 10);

  textSize(20);
  text('Move: Arrow Keys or W/A/S/D', width / 2, height / 2 + 40);
  text('Press ENTER to Start', width / 2, height / 2 + 80);
}
Line-by-line explanation (11 lines)
drawPaperBackground();
Renders the paper background and ruled lines, setting the visual theme for the start screen.
fill(0);
Sets the text color to black.
textAlign(CENTER, CENTER);
Centers all subsequent text both horizontally and vertically within its bounding box.
textSize(46);
Sets the title font size to 46 pixels—large and prominent.
text('Wimpy Kid School Run', width / 2, height / 2 - 100);
Renders the game title centered horizontally and positioned 100 pixels above the vertical center.
textSize(22);
Reduces the font size to 22 pixels for the description text.
text('You are a scrawny kid in a busy school hallway.', width / 2, height / 2 - 40);
Renders the first line of the game description.
text('Dodge the bullies and grab the homework pages!', width / 2, height / 2 - 10);
Renders the second line of the game description.
textSize(20);
Further reduces font size to 20 pixels for the control instructions.
text('Move: Arrow Keys or W/A/S/D', width / 2, height / 2 + 40);
Displays the control instructions.
text('Press ENTER to Start', width / 2, height / 2 + 80);
Displays the prompt to press ENTER, positioned near the bottom of the screen.

drawGameOverScreen()

drawGameOverScreen() shows how alpha transparency (the 4th parameter of fill()) creates a semi-opaque overlay that dims the background while keeping it slightly visible. This UI pattern appears in thousands of games.

function drawGameOverScreen() {
  // dark overlay
  fill(0, 0, 0, 170);
  rectMode(CORNER);
  noStroke();
  rect(0, 0, width, height);

  fill(255);
  textAlign(CENTER, CENTER);
  textSize(46);
  text('Game Over', width / 2, height / 2 - 70);

  textSize(26);
  text('Final Score: ' + score, width / 2, height / 2 - 20);

  textSize(20);
  text('Press R to Restart', width / 2, height / 2 + 30);
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

calculation Draw semi-transparent dark overlay fill(0, 0, 0, 170); rectMode(CORNER); noStroke(); rect(0, 0, width, height);

Creates a dark semi-transparent rectangle covering the entire screen, dimming the game behind it

calculation Render game over message and score fill(255); text('Game Over', width / 2, height / 2 - 70); text('Final Score: ' + score, width / 2, height / 2 - 20); text('Press R to Restart', width / 2, height / 2 + 30);

Displays 'Game Over', the final score, and restart instructions in white text over the dark overlay

fill(0, 0, 0, 170);
Sets the fill color to black with an alpha (transparency) value of 170 out of 255. This creates a semi-transparent dark overlay.
rectMode(CORNER);
Changes rectangle mode so rectangles are drawn from the top-left corner instead of center. This makes it easy to draw a full-screen rectangle.
noStroke();
Disables the outline stroke so the rectangle is solid.
rect(0, 0, width, height);
Draws a rectangle from (0,0) to the bottom-right, covering the entire canvas with the semi-transparent black overlay.
fill(255);
Sets the text color to white.
textAlign(CENTER, CENTER);
Centers all text horizontally and vertically.
textSize(46);
Sets font size to 46 pixels for the 'Game Over' title.
text('Game Over', width / 2, height / 2 - 70);
Renders the game-over message centered on screen, positioned above the middle.
textSize(26);
Reduces font size to 26 pixels for the final score.
text('Final Score: ' + score, width / 2, height / 2 - 20);
Displays the score concatenated as a string, showing the player's final achievement.
textSize(20);
Further reduces font size to 20 pixels for the restart instruction.
text('Press R to Restart', width / 2, height / 2 + 30);
Displays the restart prompt, giving the player clear feedback on what to do next.

keyPressed()

keyPressed() is a p5.js event handler that fires whenever the user presses a key. It uses keyCode for special keys like ENTER and the key variable for regular characters. This is different from keyIsDown(), which checks if a key is currently held.

function keyPressed() {
  if (gameState === 'start' && keyCode === ENTER) {
    resetGame(true);
  }

  if (gameState === 'gameOver' && (key === 'r' || key === 'R')) {
    resetGame(true);
  }
}
Line-by-line explanation (4 lines)

🔧 Subcomponents:

conditional Handle ENTER on start screen if (gameState === 'start' && keyCode === ENTER) { resetGame(true); }

Detects ENTER key press on the start screen and begins the game

conditional Handle R on game over screen if (gameState === 'gameOver' && (key === 'r' || key === 'R')) { resetGame(true); }

Detects R key press (case-insensitive) on the game over screen and restarts the game

function keyPressed() {
p5.js calls this function automatically whenever any key is pressed.
if (gameState === 'start' && keyCode === ENTER) {
Checks if the current state is 'start' AND the pressed key is ENTER (keyCode is a built-in p5.js variable holding the key code of the last key pressed).
resetGame(true);
Calls resetGame with true, which resets all game variables and sets gameState to 'playing'.
if (gameState === 'gameOver' && (key === 'r' || key === 'R')) {
Checks if the state is 'gameOver' AND the pressed key is 'r' or 'R' (lowercase or uppercase). The key variable holds the character of the last key pressed.

windowResized()

windowResized() is another p5.js event handler that helps your sketch respond to browser window changes. This is essential for responsive web games that should work on different screen sizes.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  player.x = width / 2;
  player.y = height - 120;
}
Line-by-line explanation (4 lines)
function windowResized() {
p5.js calls this function automatically whenever the browser window is resized.
resizeCanvas(windowWidth, windowHeight);
Resizes the p5.js canvas to match the new window dimensions, preventing the canvas from being cut off or leaving blank space.
player.x = width / 2;
Recalculates the player's x position to keep them centered horizontally after the window resize.
player.y = height - 120;
Resets the player's y position to maintain their proper position at the bottom of the new window height.

📦 Key Variables

player object

Stores the player character's data: x/y position, width/height dimensions (hitbox), and movement speed. Updated by handlePlayerMovement() and drawn by drawPlayer().

let player = { x: 200, y: 300, w: 40, h: 80, speed: 7 };
hazards array

An array of bully objects currently on-screen. Each bully has x, y, width, height, and speed. Objects are added by spawnHazards() and removed when they fall off-screen or hit the player.

let hazards = [{ x: 100, y: 50, w: 45, h: 60, speed: 4 }];
goodies array

An array of homework page objects currently on-screen. Each homework has x, y, width, height, and speed. Objects are added by spawnGoodies() and removed when collected or they fall off-screen.

let goodies = [{ x: 200, y: 30, w: 30, h: 30, speed: 3 }];
score number

The player's current score, initialized to 0. Increased by 5 every time homework is collected in checkCollisions(). Displayed in the top-left UI and shown on the game-over screen.

let score = 0;
lives number

The number of lives the player has remaining, starting at 3. Decreased by 1 each time the player collides with a bully. When lives reach 0, the game ends.

let lives = 3;
gameState string

A string that tracks which screen is currently showing: 'start' (title screen), 'playing' (active game), or 'gameOver' (game over screen). Controls which drawing and update functions are called each frame.

let gameState = 'start';
hazardSpawnInterval number

The number of frames that must pass between each bully spawn. Starts at 55 and decreases by 0.05 each spawn, making bullies appear more frequently and the game harder over time.

let hazardSpawnInterval = 55;
goodieSpawnInterval number

The number of frames that must pass between each homework page spawn. Starts at 110 and decreases by 0.1 each spawn, making collectibles appear more frequently as the game progresses.

let goodieSpawnInterval = 110;
lastHazardSpawnFrame number

Stores the frameCount at the last time a bully was spawned. Used by spawnHazards() to calculate elapsed time and decide when to spawn the next bully.

let lastHazardSpawnFrame = 0;
lastGoodieSpawnFrame number

Stores the frameCount at the last time homework was spawned. Used by spawnGoodies() to calculate elapsed time and decide when to spawn the next page.

let lastGoodieSpawnFrame = 0;

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

BUG updateHazards() and updateGoodies()

Objects are removed when their center passes the bottom edge (y - h/2 > height), but the collision detection happens at the hitbox center, not the edge. This means a bully could still hit the player just as it's being removed.

💡 Consider using a slightly different threshold for removal, or ensure collision checks happen before removal: change the removal condition to y > height + 50 to give a small buffer zone.

PERFORMANCE spawnHazards() and spawnGoodies()

Every frame, the spawn intervals decrease slightly (hazardSpawnInterval -= 0.05 and goodieSpawnInterval -= 0.1). These operations happen even when spawning doesn't occur, and the variables can become decimal values that cause slight inconsistencies.

💡 Only decrease the intervals when a spawn actually happens, or store the spawn count and decrease based on that instead of frame-by-frame changes.

STYLE rectOverlap() function

The function uses a complex negative condition with multiple || operators (return !(...)), which is hard to read at a glance.

💡 Consider refactoring to a positive condition: 'return a.x + a.w/2 >= b.x - b.w/2 && ...' etc. or add a detailed comment explaining the AABB logic.

FEATURE Game mechanics

There's no pause button during gameplay, and no sound effects or background music.

💡 Add a 'paused' game state triggered by pressing SPACE, and consider using p5.sound to add audio feedback for collisions and collectibles.

FEATURE Difficulty scaling

The game becomes harder only through spawn rate changes—the player character and their movement speed never change, and enemy speeds are randomized but never increase.

💡 Add additional difficulty layers: randomly increase player speed loss over time, increase hazard speeds as score climbs, or add new obstacle types at milestones (e.g., after 50 points).

🔄 Code Flow

Code flow showing setup, resetgame, draw, updategame, handleplayermovement, spawnhazards, spawngoodies, updatehazards, updategoodies, checkcollisions, rectoverlap, drawpaperbackground, drawplayer, drawgame, drawstartscreen, drawgameoverscreen, keypressed, windowresized

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> state-conditional[state conditional] state-conditional --> start-state[start state] state-conditional --> playing-state[playing state] state-conditional --> gameover-state[gameover state] start-state --> drawstartscreen[drawStartScreen] playing-state --> updategame[updateGame] playing-state --> drawgame[drawGame] gameover-state --> drawgameoverscreen[drawGameOverScreen] gameover-state --> r-restart[r-restart] updategame --> handleplayermovement[handlePlayerMovement] updategame --> spawnhazards[spawnHazards] updategame --> spawngoodies[spawnGoodies] updategame --> updatehazards[updateHazards] updategame --> updategoodies[updateGoodies] updategame --> checkcollisions[checkCollisions] handleplayermovement --> horizontal-input[horizontal input] handleplayermovement --> vertical-input[vertical input] handleplayermovement --> boundary-constraints[boundary constraints] spawnhazards --> spawn-check[spawn check] spawn-check --> random-size[random size] spawn-check --> bully-object[bully object] spawngoodies --> goodie-spawn-check[goodie spawn check] goodie-spawn-check --> goodie-object-create[goodie object create] updatehazards --> reverse-for-loop[reverse for loop] reverse-for-loop --> position-update[position update] reverse-for-loop --> offscreen-removal[offscreen removal] offscreen-removal --> hazard-collision-loop[hazard collision loop] updategoodies --> reverse-for-loop[reverse for loop] reverse-for-loop --> goodie-collision-loop[goodie collision loop] checkcollisions --> rectoverlap[rectOverlap] drawgame --> drawpaperbackground[drawPaperBackground] drawgame --> drawplayer[drawPlayer] drawgame --> bullies-loop[bullies loop] drawgame --> goodies-loop[goodies loop] drawgame --> ui-text[ui text] drawpaperbackground --> background-color[background color] drawpaperbackground --> notebook-lines[notebook lines] drawpaperbackground --> margin-line[margin line] drawplayer --> mode-setup[mode setup] drawplayer --> stick-figure-body[stick figure body] drawplayer --> head-and-face[head and face] gameover-state --> dark-overlay[dark overlay] dark-overlay --> gameover-text[gameover text] start --> click setup href "#fn-setup" click draw href "#fn-draw" click state-conditional href "#sub-state-conditional" click start-state href "#sub-start-state" click playing-state href "#sub-playing-state" click gameover-state href "#sub-gameover-state" click handleplayermovement href "#fn-handleplayermovement" click spawnhazards href "#fn-spawnhazards" click spawngoodies href "#fn-spawngoodies" click updatehazards href "#fn-updatehazards" click updategoodies href "#fn-updategoodies" click checkcollisions href "#fn-checkcollisions" click rectoverlap href "#fn-rectoverlap" click drawpaperbackground href "#fn-drawpaperbackground" click drawplayer href "#fn-drawplayer" click drawgame href "#fn-drawgame" click drawstartscreen href "#fn-drawstartscreen" click drawgameoverscreen href "#fn-drawgameoverscreen" click keypressed href "#fn-keypressed" click windowresized href "#fn-windowresized" click array-reset href "#sub-array-reset" click score-lives-reset href "#sub-score-lives-reset" click spawn-interval-reset href "#sub-spawn-interval-reset" click horizontal-input href "#sub-horizontal-input" click vertical-input href "#sub-vertical-input" click boundary-constraints href "#sub-boundary-constraints" click spawn-check href "#sub-spawn-check" click random-size href "#sub-random-size" click bully-object href "#sub-bully-object" click difficulty-increase href "#sub-difficulty-increase" click goodie-spawn-check href "#sub-goodie-spawn-check" click goodie-object-create href "#sub-goodie-object-create" click reverse-for-loop href "#sub-reverse-for-loop" click position-update href "#sub-position-update" click offscreen-removal href "#sub-offscreen-removal" click hazard-collision-loop href "#sub-hazard-collision-loop" click goodie-collision-loop href "#sub-goodie-collision-loop" click right-left-test href "#sub-right-left-test" click left-right-test href "#sub-left-right-test" click top-bottom-test href "#sub-top-bottom-test" click bottom-top-test href "#sub-bottom-top-test" click background-color href "#sub-background-color" click notebook-lines href "#sub-notebook-lines" click margin-line href "#sub-margin-line" click mode-setup href "#sub-mode-setup" click stick-figure-body href "#sub-stick-figure-body" click head-and-face href "#sub-head-and-face" click background-draw href "#sub-background-draw" click bullies-loop href "#sub-bullies-loop" click goodies-loop href "#sub-goodies-loop" click ui-text href "#sub-ui-text" click dark-overlay href "#sub-dark-overlay" click gameover-text href "#sub-gameover-text" click enter-start href "#sub-enter-start" click r-restart href "#sub-r-restart"

❓ Frequently Asked Questions

What kind of visual experience does the Wimpy Game sketch provide?

The Wimpy Game sketch creates a vibrant and engaging visual experience where players navigate a character through a school hallway, avoiding bullies while collecting homework pages.

How can players interact with the Wimpy Game?

Players can move the character using arrow keys or WASD, start the game by pressing Enter, and restart after a game over by pressing R.

What creative coding concepts are demonstrated in the Wimpy Game sketch?

The sketch showcases game state management, collision detection, and object spawning techniques, all fundamental to creating interactive games.

Preview

the wimpy game - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of the wimpy game - Code flow showing setup, resetgame, draw, updategame, handleplayermovement, spawnhazards, spawngoodies, updatehazards, updategoodies, checkcollisions, rectoverlap, drawpaperbackground, drawplayer, drawgame, drawstartscreen, drawgameoverscreen, keypressed, windowresized
Code Flow Diagram