lit fun

This sketch creates a 3D fighting game where players control Spider-Man to battle an AI-controlled Deadpool in a procedurally generated city. The game features full 3D movement, web-swinging mechanics, attack systems, health tracking, and a dynamic third-person camera that players control with the mouse.

🧪 Try This!

Experiment with the code by making these changes:

  1. Change the sky color — The background() call sets the sky—try bright pink or dark purple for a dramatic atmosphere
  2. Make Spider-Man invincible — Comment out the health check to prevent the game from ever ending
  3. Increase attack damage — Change the attack damage constants to one-shot enemies or make fights last longer
  4. Speed up Deadpool's AI — Increase Deadpool's movement speed to make the enemy more challenging
  5. Increase gravity for floatier feel — Higher gravity values make characters fall faster and jumps feel heavier; lower values let them float
  6. Paint Deadpool blue — Change the Deadpool model's red body color to a different color like blue
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a fully playable 3D fighting game where you control Spider-Man to defeat an AI-controlled Deadpool opponent. The game uses p5.js's WEBGL mode to render 3D characters and environments, implements gravity and jumping physics, adds a responsive third-person camera controlled by mouse movement, and includes combat mechanics with attack ranges and health tracking. Everything runs smoothly in the browser with procedurally generated city buildings as the backdrop.

The code is organized into two character objects (spiderman and deadpool) that encapsulate their own properties and behavior, a game state system that manages start/playing/gameOver screens, and helper functions that handle rendering, collision, and interaction. By studying this sketch, you'll learn how to build complex game mechanics in p5.js: 3D object-oriented design, keyboard and mouse input handling, WEBGL camera control, physics simulation, and UI rendering in 3D space.

⚙️ How It Works

  1. When the sketch loads, setup() creates a WEBGL canvas, asynchronously loads a custom font, generates random city buildings, and initializes both characters at opposite sides of the arena at ground level.
  2. The draw() loop continuously runs at 60 fps: it sets the sky background, positions the 3D camera behind Spider-Man, rotates the camera based on mouse movement, and draws the city and ground.
  3. While in 'playing' state, Spider-Man's position updates based on keyboard input (WASD to move, Space to jump) and mouse clicks trigger web swinging; Deadpool uses simple AI to chase Spider-Man and attack when close enough.
  4. Both characters are drawn as 3D spheres with eyes and body parts, and green/red health bars appear above each character showing remaining health.
  5. When either character's health reaches zero, the game transitions to 'gameOver' state and displays a winner message; clicking or pressing any key resets the game and returns to 'playing' state.
  6. The camera smoothly follows Spider-Man from behind and rotates when the player moves the mouse left/right, creating an immersive third-person perspective.

🎓 Concepts You'll Learn

WEBGL 3D renderingObject-oriented game designGame state managementPhysics simulation (gravity, velocity)Third-person camera controlAI pathfinding and behaviorAttack range and cooldown systemsHealth tracking and UI bars

📝 Code Breakdown

setup()

setup() runs once when the sketch loads. It configures the canvas, camera, and all initial game state. The asynchronous font loading allows the sketch to start immediately even if the internet is slow.

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  textSize(24);
  textAlign(CENTER, CENTER);
  perspective(Math.PI / 3, width / height, 1, 10000);
  setAttributes({ depth: true });

  // Load font ASYNC — sketch starts immediately, text appears once font loads
  loadFont(
    'https://cdn.jsdelivr.net/fontsource/fonts/vt323@latest/latin-400-normal.woff',
    function(f) { gameFont = f; textFont(f); },
    function(err) { console.warn('Font failed to load, using fallback'); }
  );

  generateBuildings();
  resetGame();
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

initialization Canvas and WEBGL Setup createCanvas(windowWidth, windowHeight, WEBGL);

Creates a WEBGL canvas that fills the entire window, enabling 3D rendering

initialization Camera Perspective perspective(Math.PI / 3, width / height, 1, 10000);

Sets up 3D perspective with 60-degree field of view and depth range from 1 to 10000 units

async-operation Asynchronous Font Loading loadFont('https://cdn.jsdelivr.net/fontsource/fonts/vt323@latest/latin-400-normal.woff', function(f) { gameFont = f; textFont(f); }, function(err) { console.warn('Font failed to load, using fallback'); });

Loads a custom font from a CDN without blocking sketch execution; gracefully falls back if font fails

createCanvas(windowWidth, windowHeight, WEBGL);
Creates a WEBGL canvas (3D mode) that stretches to fill the entire browser window
textSize(24);
Sets the default text size to 24 pixels for any text drawn on screen
textAlign(CENTER, CENTER);
Centers all text both horizontally and vertically around its coordinate
perspective(Math.PI / 3, width / height, 1, 10000);
Configures the 3D camera's perspective with a 60-degree field of view; only objects between depth 1 and 10000 are visible
setAttributes({ depth: true });
Enables depth testing so closer 3D objects appear in front of farther ones
loadFont('https://cdn.jsdelivr.net/fontsource/fonts/vt323@latest/latin-400-normal.woff', function(f) { gameFont = f; textFont(f); }, function(err) { console.warn('Font failed to load, using fallback'); });
Asynchronously loads a custom retro font from the internet; if it loads, use it; if it fails, the sketch keeps running without the custom font
generateBuildings();
Calls the helper function to create random city buildings
resetGame();
Initializes both characters' positions, health, and velocities to starting values

draw()

draw() runs 60 times per second and is the heartbeat of the game. It updates positions, applies camera logic, checks win conditions, and renders everything. Notice how the camera follows the player using sin() and cos() to create a smooth circular orbit.

🔬 These three lines calculate where the camera is positioned relative to Spider-Man. What happens if you change CAMERA_DISTANCE to 500 or 100? Why does the view feel different?

  camX = spiderman.x - cos(camAngle) * CAMERA_DISTANCE;
  camY = spiderman.y - 100;
  camZ = spiderman.z - sin(camAngle) * CAMERA_DISTANCE;
function draw() {
  background(135, 180, 220); // Light sky blue

  lights();

  camX = spiderman.x - cos(camAngle) * CAMERA_DISTANCE;
  camY = spiderman.y - 100;
  camZ = spiderman.z - sin(camAngle) * CAMERA_DISTANCE;
  camLookX = spiderman.x;
  camLookY = spiderman.y;
  camLookZ = spiderman.z;

  camera(camX, camY, camZ, camLookX, camLookY, camLookZ, 0, 1, 0);

  camAngle += (mouseX - pmouseX) * CAM_ROTATION_SPEED;

  drawCityBackground();
  drawGround();

  if (gameState === 'start') {
    displayStartScreen();
  } else if (gameState === 'playing') {
    spiderman.move();
    deadpool.move(spiderman.x, spiderman.y, spiderman.z);

    spiderman.display();
    deadpool.display();

    displayHealthBar(spiderman.x, spiderman.y - SPIDERMAN_SIZE, spiderman.z, spiderman.health, SPIDERMAN_HEALTH, [0, 200, 0]);
    displayHealthBar(deadpool.x, deadpool.y - DEADPOOL_SIZE, deadpool.z, deadpool.health, DEADPOOL_HEALTH, [200, 0, 0]);

    if (spiderman.health <= 0 || deadpool.health <= 0) {
      gameState = 'gameOver';
    }
  } else if (gameState === 'gameOver') {
    displayGameOverScreen();
  }
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

calculation Third-Person Camera Position camX = spiderman.x - cos(camAngle) * CAMERA_DISTANCE;

Calculates camera X position orbiting around Spider-Man using cosine and the camera angle

function-call Camera Function Call camera(camX, camY, camZ, camLookX, camLookY, camLookZ, 0, 1, 0);

Positions the 3D camera at (camX, camY, camZ) looking toward Spider-Man, with Y-axis pointing up

calculation Mouse-Based Camera Rotation camAngle += (mouseX - pmouseX) * CAM_ROTATION_SPEED;

Updates the camera orbit angle based on how much the mouse moved horizontally since last frame

conditional Game State Branch if (gameState === 'start') { ... } else if (gameState === 'playing') { ... } else if (gameState === 'gameOver') { ... }

Executes different code blocks depending on whether the game is starting, in progress, or finished

conditional Win Condition if (spiderman.health <= 0 || deadpool.health <= 0) { gameState = 'gameOver'; }

Ends the game when either character's health drops to zero or below

background(135, 180, 220); // Light sky blue
Clears the canvas each frame with a light blue color, creating the sky
lights();
Enables default 3D lighting so spheres and boxes are shaded realistically
camX = spiderman.x - cos(camAngle) * CAMERA_DISTANCE;
Positions the camera to the left/right of Spider-Man; the cosine function makes it orbit based on camera angle
camY = spiderman.y - 100;
Positions the camera 100 pixels above Spider-Man's vertical position
camZ = spiderman.z - sin(camAngle) * CAMERA_DISTANCE;
Positions the camera forward/backward relative to Spider-Man using the sine function
camLookX = spiderman.x; camLookY = spiderman.y; camLookZ = spiderman.z;
The camera always looks directly at Spider-Man's position
camera(camX, camY, camZ, camLookX, camLookY, camLookZ, 0, 1, 0);
Applies the camera: places it at (camX, camY, camZ), aims it at Spider-Man, with Y pointing up (0, 1, 0)
camAngle += (mouseX - pmouseX) * CAM_ROTATION_SPEED;
When you move the mouse horizontally, the camera rotates around Spider-Man; pmouseX is the previous frame's mouse position
drawCityBackground(); drawGround();
Draws all the buildings and the ground plane before drawing characters
if (gameState === 'start') { displayStartScreen(); }
If the game hasn't started yet, show the title and instructions
spiderman.move(); deadpool.move(spiderman.x, spiderman.y, spiderman.z);
Updates both characters' positions based on physics, input, and AI
spiderman.display(); deadpool.display();
Draws both characters as 3D shapes at their updated positions
displayHealthBar(...); displayHealthBar(...);
Draws green health bar for Spider-Man and red health bar for Deadpool above each character
if (spiderman.health <= 0 || deadpool.health <= 0) { gameState = 'gameOver'; }
Checks if either character is dead; if so, transitions to the game over state

spiderman.display()

This function draws Spider-Man's 3D model every frame at his current position. It uses push/pop to isolate transformations, rotates the model to face the camera, and conditionally draws a web line when swinging. The legs use sin/cos in a loop to distribute them evenly in a circle—a common pattern for creating radial arrangements.

display: function() {
  push();
  translate(this.x, this.y, this.z);
  rotateY(camAngle);

  // Body (Red sphere)
  fill(200, 0, 0);
  noStroke();
  sphere(SPIDERMAN_SIZE / 2);

  // Eyes (White planes)
  fill(255);
  noStroke();
  push();
  translate(-SPIDERMAN_SIZE / 5, -SPIDERMAN_SIZE / 10, SPIDERMAN_SIZE / 2);
  rotateY(HALF_PI);
  plane(SPIDERMAN_SIZE / 4, SPIDERMAN_SIZE / 6);
  pop();
  push();
  translate(SPIDERMAN_SIZE / 5, -SPIDERMAN_SIZE / 10, SPIDERMAN_SIZE / 2);
  rotateY(HALF_PI);
  plane(SPIDERMAN_SIZE / 4, SPIDERMAN_SIZE / 6);
  pop();

  // Spider symbol (Blue)
  fill(0, 0, 200);
  noStroke();
  push();
  translate(0, SPIDERMAN_SIZE / 5, -SPIDERMAN_SIZE / 2);
  sphere(SPIDERMAN_SIZE / 3, 10, 10);
  pop();

  // Legs
  stroke(0, 0, 200);
  strokeWeight(2);
  fill(0, 0, 200);
  for (let i = 0; i < 4; i++) {
    let angleOffset = Math.PI / 4 * i;
    let legX = cos(angleOffset) * SPIDERMAN_SIZE / 3;
    let legY = sin(angleOffset) * SPIDERMAN_SIZE / 3;
    push();
    translate(legX, legY, -SPIDERMAN_SIZE / 2);
    rotateX(Math.PI / 2);
    cylinder(2, SPIDERMAN_SIZE / 2);
    pop();
  }

  pop();

  // Draw web line if swinging
  if (this.isSwinging) {
    stroke(200, 200, 255);
    strokeWeight(2);
    line(this.x, this.y, this.z, this.webTargetX, this.webTargetY, this.webTargetZ);
  }
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

shape Main Body sphere(SPIDERMAN_SIZE / 2);

Draws a red sphere as Spider-Man's main body

coordinate-setup Eyes Positioning translate(-SPIDERMAN_SIZE / 5, -SPIDERMAN_SIZE / 10, SPIDERMAN_SIZE / 2);

Positions each eye on the front face of the body relative to the center

shape Spider Symbol on Back translate(0, SPIDERMAN_SIZE / 5, -SPIDERMAN_SIZE / 2);

Places a blue spider symbol on the back of the body

for-loop Four Legs for (let i = 0; i < 4; i++) { ... }

Draws four cylindrical legs radiating outward from the body at 90-degree intervals

conditional Web Swing Line if (this.isSwinging) { ... }

When swinging, draws a visible line from Spider-Man to the web target point

push();
Saves the current transformation state so changes below don't affect other objects
translate(this.x, this.y, this.z);
Moves the coordinate system to Spider-Man's position so all shapes draw relative to that point
rotateY(camAngle);
Rotates Spider-Man to face the camera as it orbits, keeping him always visible
fill(200, 0, 0); noStroke(); sphere(SPIDERMAN_SIZE / 2);
Draws a red sphere with diameter SPIDERMAN_SIZE as the main body
fill(255); noStroke();
Changes color to white for the eyes
translate(-SPIDERMAN_SIZE / 5, -SPIDERMAN_SIZE / 10, SPIDERMAN_SIZE / 2);
Moves to the left side of the face to place the left eye
rotateY(HALF_PI); plane(SPIDERMAN_SIZE / 4, SPIDERMAN_SIZE / 6);
Rotates the plane 90 degrees and draws a white rectangle for the left eye
for (let i = 0; i < 4; i++) { let angleOffset = Math.PI / 4 * i; ... }
Loops 4 times, each iteration rotating by 90 degrees (Math.PI / 4 radians) to position legs evenly around the body
let legX = cos(angleOffset) * SPIDERMAN_SIZE / 3; let legY = sin(angleOffset) * SPIDERMAN_SIZE / 3;
Uses cosine and sine to calculate the X and Y position of each leg on a circle
translate(legX, legY, -SPIDERMAN_SIZE / 2); rotateX(Math.PI / 2); cylinder(2, SPIDERMAN_SIZE / 2);
Moves to the leg position, rotates so it stands upright, and draws a thin blue cylinder
if (this.isSwinging) { stroke(200, 200, 255); strokeWeight(2); line(this.x, this.y, this.z, this.webTargetX, this.webTargetY, this.webTargetZ); }
If Spider-Man is swinging, draws a light-blue line from his position to the web target to show the swing direction

spiderman.move()

This is the most complex function in the sketch. It handles both normal movement (keyboard-driven) and web-swinging (physics-driven). The key insight is that swinging uses distance-based force pulling toward the target, while normal movement uses direct velocity. Both branches apply gravity and constrain position to keep Spider-Man in bounds.

🔬 These lines calculate the distance to the web target. What happens if you remove the 'if (d < 1)' line? Why is it there?

    let dx = this.webTargetX - this.x;
    let dy = this.webTargetY - this.y;
    let dz = this.webTargetZ - this.z;
    let d = sqrt(dx * dx + dy * dy + dz * dz);
    if (d < 1) d = 1;
move: function() {
  if (this.isSwinging) {
    let dx = this.webTargetX - this.x;
    let dy = this.webTargetY - this.y;
    let dz = this.webTargetZ - this.z;
    let d = sqrt(dx * dx + dy * dy + dz * dz);
    if (d < 1) d = 1;

    let force = d * 0.05;
    let swingAccelX = dx / d * force;
    let swingAccelY = dy / d * force;
    let swingAccelZ = dz / d * force;

    swingAccelY += GRAVITY;

    this.swingVX += swingAccelX;
    this.swingVY += swingAccelY;
    this.swingVZ += swingAccelZ;

    this.swingVX *= 0.98;
    this.swingVY *= 0.98;
    this.swingVZ *= 0.98;

    this.x += this.swingVX;
    this.y += this.swingVY;
    this.z += this.swingVZ;

    this.x = constrain(this.x, -width / 2 + SPIDERMAN_SIZE / 2, width / 2 - SPIDERMAN_SIZE / 2);
    this.y = constrain(this.y, SPIDERMAN_SIZE / 2, height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2);
    this.z = constrain(this.z, -1000, 1000);

    this.vx = 0;
    this.vy = 0;
    this.vz = 0;
  } else {
    if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) {
      this.vx = -SPIDERMAN_SPEED;
    } else if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) {
      this.vx = SPIDERMAN_SPEED;
    } else {
      this.vx *= 0.8;
    }

    if (keyIsDown(UP_ARROW) || keyIsDown(87)) {
      this.vz = -SPIDERMAN_SPEED;
    } else if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) {
      this.vz = SPIDERMAN_SPEED;
    } else {
      this.vz *= 0.8;
    }

    this.x += this.vx;
    this.z += this.vz;

    this.vy += GRAVITY;
    this.y += this.vy;

    if (this.y > height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2) {
      this.y = height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2;
      this.vy = 0;
      this.isJumping = false;
      this.vx *= 0.5;
      this.vz *= 0.5;
      this.swingVX = 0;
      this.swingVY = 0;
      this.swingVZ = 0;
    }

    this.x = constrain(this.x, -width / 2 + SPIDERMAN_SIZE / 2, width / 2 - SPIDERMAN_SIZE / 2);
    this.z = constrain(this.z, -1000, 1000);
  }
}
Line-by-line explanation (15 lines)

🔧 Subcomponents:

conditional Web Swing Physics if (this.isSwinging) { ... }

When swinging, calculates attraction toward the web target and applies gravity to create pendulum-like motion

calculation Distance to Web Target let d = sqrt(dx * dx + dy * dy + dz * dz);

Uses the distance formula to calculate how far Spider-Man is from the web target

calculation Swing Acceleration let force = d * 0.05; let swingAccelX = dx / d * force;

Calculates how strongly Spider-Man is pulled toward the web target based on distance

calculation Swing Friction this.swingVX *= 0.98;

Reduces swing velocity each frame to create realistic damping (0.98 = 2% loss per frame)

conditional Keyboard Movement if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) { this.vx = -SPIDERMAN_SPEED; }

Checks for left arrow or 'A' key and sets horizontal velocity accordingly

calculation Gravity and Jumping this.vy += GRAVITY; this.y += this.vy;

Increases downward velocity each frame and updates vertical position

conditional Ground Collision Detection if (this.y > height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2) { ... }

Checks if Spider-Man has fallen below ground level and stops him there

if (this.isSwinging) {
Separate physics apply when swinging vs. normal movement
let dx = this.webTargetX - this.x; let dy = this.webTargetY - this.y; let dz = this.webTargetZ - this.z;
Calculates the vector (difference) between Spider-Man and the web target in all three dimensions
let d = sqrt(dx * dx + dy * dy + dz * dz);
Calculates the 3D distance using the Pythagorean theorem
if (d < 1) d = 1;
Prevents division by zero; ensures distance is at least 1
let force = d * 0.05;
The farther away the target, the stronger the pull (0.05 is a tuning constant)
let swingAccelX = dx / d * force;
Normalizes the direction (dx / d) and applies the force to create acceleration toward the target
swingAccelY += GRAVITY;
Adds gravity to the vertical acceleration so Spider-Man falls while swinging
this.swingVX *= 0.98;
Multiplies velocity by 0.98 each frame, causing the swing to slow down and eventually stop (2% loss)
this.x += this.swingVX;
Updates position by adding velocity
if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) { this.vx = -SPIDERMAN_SPEED; }
Checks if left arrow or 'A' is pressed; if so, set horizontal velocity to move left
this.vx *= 0.8;
When no key is pressed, reduce horizontal velocity to create smooth deceleration (friction)
this.vy += GRAVITY; this.y += this.vy;
Applies gravity (accelerates downward) and moves Spider-Man based on vertical velocity
if (this.y > height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2) {
Checks if Spider-Man has fallen below the ground level
this.y = height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2; this.vy = 0;
Stops Spider-Man at ground level and sets vertical velocity to 0 so he stops falling
this.isJumping = false;
Allows jumping again since Spider-Man is back on the ground

spiderman.attack()

This function demonstrates attack cooldown (prevent spam) and range checking. The visual pulse effect makes the attack feel impactful. The cooldown is essential in games to balance combat and prevent instant wins.

attack: function(target) {
  if (millis() - this.lastAttackTime > this.attackCooldown) {
    let d = dist(this.x, this.y, this.z, target.x, target.y, target.z);
    if (d < SPIDERMAN_ATTACK_RANGE) {
      target.health -= SPIDERMAN_ATTACK_DAMAGE;
      this.lastAttackTime = millis();
      push();
      translate(this.x, this.y, this.z);
      fill(255, 0, 0, 100);
      noStroke();
      sphere(SPIDERMAN_ATTACK_RANGE * 2, 10, 10);
      pop();
    }
  }
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

conditional Attack Cooldown if (millis() - this.lastAttackTime > this.attackCooldown) {

Prevents Spider-Man from attacking more than once per 500 milliseconds

conditional Attack Range Check if (d < SPIDERMAN_ATTACK_RANGE) {

Only damages Deadpool if he's within attacking distance

calculation Damage Subtraction target.health -= SPIDERMAN_ATTACK_DAMAGE;

Reduces Deadpool's health by 10 points

shape Attack Pulse Effect sphere(SPIDERMAN_ATTACK_RANGE * 2, 10, 10);

Draws a red semi-transparent sphere to show where the attack hit

if (millis() - this.lastAttackTime > this.attackCooldown) {
Checks if enough time (500 ms) has passed since the last attack; prevents spam
let d = dist(this.x, this.y, this.z, target.x, target.y, target.z);
Calculates 3D distance from Spider-Man to Deadpool
if (d < SPIDERMAN_ATTACK_RANGE) {
Only damages if Deadpool is within 60 pixels of Spider-Man
target.health -= SPIDERMAN_ATTACK_DAMAGE;
Subtracts 10 from Deadpool's health
this.lastAttackTime = millis();
Records the current time as the last attack time so the cooldown starts
sphere(SPIDERMAN_ATTACK_RANGE * 2, 10, 10);
Draws a semi-transparent red sphere centered on Spider-Man to visualize the attack

deadpool.move()

Deadpool's AI is intentionally simple: chase Spider-Man by moving closer on each axis, apply gravity, and attack when in range. This creates believable but not overwhelming opposition. More advanced AI could predict Spider-Man's position or jump to reach him.

move: function(targetX, targetY, targetZ) {
  if (this.x < targetX) this.x += DEADPOOL_SPEED;
  else if (this.x > targetX) this.x -= DEADPOOL_SPEED;

  if (this.z < targetZ) this.z += DEADPOOL_SPEED;
  else if (this.z > targetZ) this.z -= DEADPOOL_SPEED;

  this.vy += GRAVITY;
  this.y += this.vy;

  if (this.y > height / 2 - GROUND_LEVEL - DEADPOOL_SIZE / 2) {
    this.y = height / 2 - GROUND_LEVEL - DEADPOOL_SIZE / 2;
    this.vy = 0;
  }

  this.x = constrain(this.x, -width / 2 + DEADPOOL_SIZE / 2, width / 2 - DEADPOOL_SIZE / 2);
  this.z = constrain(this.z, -1000, 1000);

  let d = dist(this.x, this.y, this.z, targetX, targetY, targetZ);
  if (d < DEADPOOL_ATTACK_RANGE) {
    this.attack(spiderman);
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

conditional Simple AI Chase if (this.x < targetX) this.x += DEADPOOL_SPEED;

Moves Deadpool toward Spider-Man's position at constant speed

calculation Gravity and Falling this.vy += GRAVITY; this.y += this.vy;

Applies gravity and updates vertical position

conditional Ground Collision if (this.y > height / 2 - GROUND_LEVEL - DEADPOOL_SIZE / 2) {

Stops Deadpool from falling through the ground

conditional Attack Trigger if (d < DEADPOOL_ATTACK_RANGE) { this.attack(spiderman); }

Automatically attacks Spider-Man when close enough

if (this.x < targetX) this.x += DEADPOOL_SPEED;
If Spider-Man is to the right, move Deadpool right toward him
else if (this.x > targetX) this.x -= DEADPOOL_SPEED;
If Spider-Man is to the left, move Deadpool left toward him
if (this.z < targetZ) this.z += DEADPOOL_SPEED;
If Spider-Man is further away, move Deadpool forward
else if (this.z > targetZ) this.z -= DEADPOOL_SPEED;
If Spider-Man is closer, move Deadpool backward
this.vy += GRAVITY; this.y += this.vy;
Applies gravity and updates Deadpool's height
if (this.y > height / 2 - GROUND_LEVEL - DEADPOOL_SIZE / 2) { this.y = height / 2 - GROUND_LEVEL - DEADPOOL_SIZE / 2; this.vy = 0; }
Stops Deadpool at ground level and resets vertical velocity
let d = dist(this.x, this.y, this.z, targetX, targetY, targetZ);
Calculates distance to Spider-Man
if (d < DEADPOOL_ATTACK_RANGE) { this.attack(spiderman); }
If close enough, automatically attack (simple AI behavior)

generateBuildings()

This function generates a procedurally randomized city by creating tall rectangles and placing them in rows that extend into the distance. Each time the canvas resizes or the game resets, a new city is generated. The nested loops fill width first, then depth, creating a grid-like street layout.

🔬 This code controls the city size. What happens if you change height * 0.3 to height * 0.1 (much shorter buildings) or height * 1.0 (super tall buildings)? What if you change the 200 spacing to 100?

  while (xOffset < width / 2) {
    let bh = random(height * 0.3, height * 0.7);
    let bw = random(80, 120);
    for (let zPos = -1000; zPos <= 1000; zPos += 200) {
function generateBuildings() {
  buildings = [];
  let xOffset = -width / 2;
  while (xOffset < width / 2) {
    let bh = random(height * 0.3, height * 0.7);
    let bw = random(80, 120);
    for (let zPos = -1000; zPos <= 1000; zPos += 200) {
      buildings.push({ x: xOffset, y: height / 2 - GROUND_LEVEL - bh / 2, z: zPos, w: bw, h: bh, d: bw / 2 });
    }
    xOffset += bw + random(20, 50);
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

initialization Buildings Array Reset buildings = [];

Clears the old buildings array and starts fresh

while-loop Horizontal Building Placement while (xOffset < width / 2) {

Creates buildings across the full width of the screen

for-loop Depth Building Placement for (let zPos = -1000; zPos <= 1000; zPos += 200) {

Places copies of each building at different depths to create a city street

object-creation Building Data buildings.push({ x: xOffset, y: height / 2 - GROUND_LEVEL - bh / 2, z: zPos, w: bw, h: bh, d: bw / 2 });

Stores building position, dimensions, and depth as an object

buildings = [];
Empties the buildings array so we can create a fresh set
let xOffset = -width / 2;
Starts placing buildings at the left edge of the canvas
while (xOffset < width / 2) {
Keeps creating buildings until we've covered the full width
let bh = random(height * 0.3, height * 0.7);
Each building gets a random height between 30% and 70% of screen height
let bw = random(80, 120);
Each building gets a random width between 80 and 120 pixels
for (let zPos = -1000; zPos <= 1000; zPos += 200) {
For each building, create 11 copies at different depths (z positions) spaced 200 pixels apart
buildings.push({ x: xOffset, y: height / 2 - GROUND_LEVEL - bh / 2, z: zPos, w: bw, h: bh, d: bw / 2 });
Adds a new building object with position (x, y, z), width (w), height (h), and depth (d) to the array
xOffset += bw + random(20, 50);
Moves to the next building position; adds a random gap between buildings

keyPressed()

keyPressed() is called whenever any key is pressed. Using keyCode (the unique number for each key) allows checking for specific keys. This function branches behavior based on the game state: during play, it handles game commands; at start/end screens, any key starts a new game.

function keyPressed() {
  if (gameState === 'playing') {
    if (keyCode === 32) spiderman.jump();
    if (keyCode === 69) spiderman.attack(deadpool);
  } else if (gameState === 'start' || gameState === 'gameOver') {
    resetGame();
    gameState = 'playing';
  }
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

conditional Space Bar to Jump if (keyCode === 32) spiderman.jump();

Detects Space key (keyCode 32) and calls jump function

conditional E Key to Attack if (keyCode === 69) spiderman.attack(deadpool);

Detects E key (keyCode 69) and triggers an attack

conditional Any Key to Start } else if (gameState === 'start' || gameState === 'gameOver') { resetGame(); gameState = 'playing'; }

Pressing any key during start or game over screens resets and starts the game

if (gameState === 'playing') {
Only respond to keys if the game is actively running
if (keyCode === 32) spiderman.jump();
keyCode 32 is the Space bar; call the jump function
if (keyCode === 69) spiderman.attack(deadpool);
keyCode 69 is the E key; trigger an attack on Deadpool
} else if (gameState === 'start' || gameState === 'gameOver') {
If at start or game over screen, pressing any key starts the game
resetGame();
Reset all positions and health
gameState = 'playing';
Change game state to playing

mousePressed()

mousePressed() is called once per mouse click. During gameplay, it initiates web swinging; on menu screens, it starts the game. This provides an intuitive control mechanism: click where you want to swing.

function mousePressed() {
  if (gameState === 'playing') {
    spiderman.startSwing(mouseX, mouseY);
  } else if (gameState === 'start' || gameState === 'gameOver') {
    resetGame();
    gameState = 'playing';
  }
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

function-call Web Swing Initiation spiderman.startSwing(mouseX, mouseY);

Starts a web swing toward the mouse position when clicked during gameplay

conditional Start Screen Click Handler } else if (gameState === 'start' || gameState === 'gameOver') { resetGame(); gameState = 'playing'; }

Clicking on start or game over screens starts a new game

if (gameState === 'playing') {
Only respond to clicks if the game is running
spiderman.startSwing(mouseX, mouseY);
Click position determines where Spider-Man will swing to; mouseX and mouseY are p5.js variables for cursor position
} else if (gameState === 'start' || gameState === 'gameOver') {
If at start or game over screen, any click begins a new game

mouseReleased()

mouseReleased() fires when the mouse button is released. This pair with mousePressed() creates the click-and-hold swing mechanic: press to swing, hold to stay airborne, release to land with momentum.

function mouseReleased() {
  if (gameState === 'playing') {
    spiderman.endSwing();
  }
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

function-call Web Swing End spiderman.endSwing();

Stops the web swing when the mouse button is released

if (gameState === 'playing') {
Only process mouse release if the game is running
spiderman.endSwing();
Releases the web swing and converts swing velocity to regular movement velocity

resetGame()

resetGame() initializes all character variables to their starting values. This function is called in setup() and whenever a new game begins. It ensures both characters start in known positions with full health and clean state.

function resetGame() {
  spiderman.x = -width / 4;
  spiderman.y = height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2;
  spiderman.z = 0;
  spiderman.vx = 0; spiderman.vy = 0; spiderman.vz = 0;
  spiderman.health = SPIDERMAN_HEALTH;
  spiderman.isJumping = false;
  spiderman.isSwinging = false;
  spiderman.swingVX = 0; spiderman.swingVY = 0; spiderman.swingVZ = 0;
  spiderman.lastAttackTime = 0;

  deadpool.x = width / 4;
  deadpool.y = height / 2 - GROUND_LEVEL - DEADPOOL_SIZE / 2;
  deadpool.z = 0;
  deadpool.vy = 0;
  deadpool.health = DEADPOOL_HEALTH;
  deadpool.lastAttackTime = 0;

  camAngle = 0;
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

initialization Spider-Man Reset spiderman.x = -width / 4;

Resets Spider-Man's position, health, and state variables

initialization Deadpool Reset deadpool.x = width / 4;

Resets Deadpool's position, health, and state variables

initialization Camera Reset camAngle = 0;

Resets the camera angle to the default view

spiderman.x = -width / 4;
Places Spider-Man at 1/4 screen width from the left side
spiderman.y = height / 2 - GROUND_LEVEL - SPIDERMAN_SIZE / 2;
Places Spider-Man on the ground at starting height
spiderman.z = 0;
Places Spider-Man at the center depth (z-axis)
spiderman.vx = 0; spiderman.vy = 0; spiderman.vz = 0;
Clears all velocities so Spider-Man starts stationary
spiderman.health = SPIDERMAN_HEALTH;
Restores Spider-Man's health to 100
spiderman.isJumping = false; spiderman.isSwinging = false;
Resets state flags so Spider-Man can jump and swing again
deadpool.x = width / 4;
Places Deadpool at 1/4 screen width from the right side (opposite of Spider-Man)
camAngle = 0;
Resets camera orbit angle to its default view

displayHealthBar()

This function creates a health bar that scales with the character's current health. The key technique is multiplying box width by healthRatio to shrink the bar. The text label shows exact health numbers. Both characters get similar bars with different colors (green/red).

function displayHealthBar(x, y, z, currentHealth, maxHealth, barColor) {
  let barWidth = 80;
  let barHeight = 8;
  let healthRatio = max(0, currentHealth / maxHealth);

  push();
  translate(x, y, z);
  rotateY(camAngle);
  noStroke();

  // Background bar
  fill(60);
  box(barWidth, barHeight, 5);

  // Health bar
  fill(barColor[0], barColor[1], barColor[2]);
  push();
  translate(-barWidth / 2 + (barWidth * healthRatio) / 2, 0, 0);
  box(barWidth * healthRatio, barHeight, 5);
  pop();

  if (gameFont) {
    fill(255);
    textSize(10);
    text(ceil(currentHealth) + "/" + maxHealth, 0, -barHeight * 2);
  }
  pop();
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

calculation Health Ratio let healthRatio = max(0, currentHealth / maxHealth);

Calculates what fraction of max health remains (0 to 1)

shape Dark Background Bar fill(60); box(barWidth, barHeight, 5);

Draws a dark gray background box showing max capacity

shape Colored Health Bar box(barWidth * healthRatio, barHeight, 5);

Draws a colored box showing current health, scaled by healthRatio

conditional Health Text if (gameFont) { ... text(...) }

Displays current/max health as text if font is loaded

let barWidth = 80; let barHeight = 8;
Sets the health bar size (80 pixels wide, 8 pixels tall)
let healthRatio = max(0, currentHealth / maxHealth);
Calculates percentage of health remaining; max(0, ...) prevents negative ratios
translate(x, y, z);
Positions the health bar in 3D space above the character
rotateY(camAngle);
Rotates the health bar to face the camera as it orbits
fill(60); box(barWidth, barHeight, 5);
Draws a dark gray background box
fill(barColor[0], barColor[1], barColor[2]);
Changes color to the passed color (green for Spider-Man, red for Deadpool)
translate(-barWidth / 2 + (barWidth * healthRatio) / 2, 0, 0);
Moves the health bar so it shrinks from the right as health decreases
box(barWidth * healthRatio, barHeight, 5);
Draws the colored health box, scaled by the health ratio
if (gameFont) { text(ceil(currentHealth) + "/" + maxHealth, 0, -barHeight * 2); }
If font loaded, displays text like '75/100' above the bar

drawCityBackground()

This function loops through the buildings array created by generateBuildings() and draws each one. The for...of loop syntax (for (let b of buildings)) is clean for iterating over array elements. Each building is drawn as a simple 3D box.

function drawCityBackground() {
  fill(120, 120, 140);
  noStroke();
  for (let b of buildings) {
    push();
    translate(b.x, b.y, b.z);
    box(b.w, b.h, b.d);
    pop();
  }
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

for-loop Building Drawing Loop for (let b of buildings) { ... }

Iterates through every building and draws it

shape Building Box box(b.w, b.h, b.d);

Draws a 3D box with the building's width, height, and depth

fill(120, 120, 140);
Sets the fill color to a purple-gray color for buildings
noStroke();
Disables outlines so buildings are solid
for (let b of buildings) {
Loops through every building in the buildings array
push();
Saves the current transformation state
translate(b.x, b.y, b.z);
Moves to the building's position in 3D space
box(b.w, b.h, b.d);
Draws a 3D box (rectangular solid) with the building's width, height, and depth
pop();
Restores the transformation state so the next building is independent

drawGround()

This function draws the ground as a flat green plane. A plane is a 2D shape, so it needs a 90-degree rotation to appear horizontal. The ground is much larger than the visible area to ensure characters never see past it.

function drawGround() {
  fill(80, 130, 80);
  noStroke();
  push();
  translate(0, height / 2 - GROUND_LEVEL / 2, 0);
  rotateX(HALF_PI);
  plane(width * 2, 2000);
  pop();
}
Line-by-line explanation (4 lines)

🔧 Subcomponents:

shape Ground Plane plane(width * 2, 2000);

Draws a 2D plane (rectangle) representing the ground surface

fill(80, 130, 80);
Sets fill color to green for the grass ground
translate(0, height / 2 - GROUND_LEVEL / 2, 0);
Positions the ground at the correct Y height
rotateX(HALF_PI);
Rotates the plane 90 degrees so it's horizontal instead of vertical
plane(width * 2, 2000);
Draws a flat rectangle twice as wide as the canvas and 2000 pixels deep

displayStartScreen()

This screen displays before the game starts. It uses conditional rendering: if the custom font loaded, display nice text; otherwise, draw placeholder boxes. The screen is positioned in front of the camera and rotated to always face it, creating a billboard effect.

function displayStartScreen() {
  push();
  translate(camLookX, camLookY, camLookZ - 400);
  rotateY(camAngle);

  // Title background panel
  fill(0, 0, 0, 150);
  noStroke();
  box(500, 350, 2);

  if (gameFont) {
    fill(255, 50, 50);
    noStroke();
    textSize(42);
    text("Spider-Man vs Deadpool", 0, -120);
    fill(255);
    textSize(22);
    text("Click or Press Any Key to Start", 0, -60);
    textSize(16);
    text("A/D: Move Left/Right", 0, -20);
    text("W/S: Move Forward/Back", 0, 10);
    text("Space: Jump  |  E: Attack", 0, 40);
    text("Mouse: Web Swing + Camera", 0, 70);
  } else {
    // Fallback: colored label boxes
    fill(255, 50, 50);
    noStroke();
    push(); translate(0, -120, 1); box(300, 40, 1); pop();
    fill(255);
    push(); translate(0, -60, 1); box(280, 25, 1); pop();
    push(); translate(0, 20, 1); box(200, 20, 1); pop();
  }
  pop();
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

shape Background Panel box(500, 350, 2);

Draws a dark semi-transparent box as background for the title

conditional Text Display if (gameFont) { ... text(...) }

If font is loaded, display title and instructions with text

conditional Fallback Display } else { ... push(); translate(...); box(...); pop(); }

If font fails to load, show colored boxes instead of text

translate(camLookX, camLookY, camLookZ - 400);
Positions the start screen 400 units in front of where the camera is looking
rotateY(camAngle);
Rotates the start screen to always face the camera
fill(0, 0, 0, 150); box(500, 350, 2);
Draws a dark semi-transparent rectangle as a background panel
if (gameFont) { ... }
If the custom font loaded successfully, display text instructions
text("Spider-Man vs Deadpool", 0, -120);
Draws the title text in red at position Y=-120
} else { ... push(); translate(0, -120, 1); box(300, 40, 1); pop(); }
If font didn't load, draw colored boxes instead so the menu is still visible

displayGameOverScreen()

This screen appears when the game ends. It uses a ternary operator (condition ? true : false) to determine the winner and displays the appropriate message. Like the start screen, it includes a fallback for missing font and rotates to face the camera.

function displayGameOverScreen() {
  push();
  translate(camLookX, camLookY, camLookZ - 400);
  rotateY(camAngle);

  fill(0, 0, 0, 180);
  noStroke();
  box(450, 200, 2);

  if (gameFont) {
    fill(255);
    noStroke();
    textSize(42);
    let msg = "Game Over! ";
    msg += spiderman.health <= 0 ? "Deadpool Wins!" : "Spider-Man Wins!";
    text(msg, 0, -40);
    textSize(22);
    text("Click or Press Any Key to Replay", 0, 20);
  } else {
    fill(255, 0, 0);
    noStroke();
    push(); translate(0, -30, 1); box(300, 40, 1); pop();
    fill(255);
    push(); translate(0, 20, 1); box(250, 25, 1); pop();
  }
  pop();
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

shape Background Panel box(450, 200, 2);

Draws a dark semi-transparent box as the game over screen background

conditional Winner Determination msg += spiderman.health <= 0 ? "Deadpool Wins!" : "Spider-Man Wins!";

Determines and displays which character won

conditional Game Over Text if (gameFont) { ... text(...) }

If font loaded, display winner message and replay instructions

translate(camLookX, camLookY, camLookZ - 400);
Positions the game over screen in front of the camera
fill(0, 0, 0, 180); box(450, 200, 2);
Draws a semi-transparent dark background panel
let msg = "Game Over! "; msg += spiderman.health <= 0 ? "Deadpool Wins!" : "Spider-Man Wins!";
Creates a message: if Spider-Man's health is 0 or less, Deadpool won; otherwise Spider-Man won
text(msg, 0, -40);
Displays the winner message in white text
text("Click or Press Any Key to Replay", 0, 20);
Instructs the player how to restart

📦 Key Variables

gameState string

Tracks which screen is active: 'start' (title), 'playing' (active game), or 'gameOver' (end screen)

let gameState = 'start';
spiderman object

Stores Spider-Man's position (x, y, z), velocity, health, state flags, and methods for display, movement, jumping, swinging, and attacking

let spiderman = { x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0, health: 100, ... };
deadpool object

Stores Deadpool's position, velocity, health, state, and methods for AI movement, display, and attacking

let deadpool = { x: 0, y: 0, z: 0, vy: 0, health: 120, ... };
buildings array

Stores an array of building objects with position (x, y, z), width, height, and depth; generated procedurally and rendered each frame

let buildings = [{ x: -100, y: 200, z: 0, w: 80, h: 300, d: 40 }, ...];
camX, camY, camZ number

The 3D camera position coordinates; updated each frame to follow Spider-Man from behind

let camX = 0, camY = 0, camZ = 0;
camAngle number

The camera's orbit angle around Spider-Man; controlled by mouse movement for third-person rotation

let camAngle = 0;
gameFont object

Stores the loaded custom font (VT323); null until font finishes loading asynchronously

let gameFont = null;

🔧 Potential Improvements (6)

Here are some ways this code could be enhanced:

BUG spiderman.move() swing physics

When swinging very fast and releasing near a wall, Spider-Man can exit the constrained bounds momentarily before being pulled back

💡 Apply position constraints immediately after updating position in both swing and normal movement paths, not just at the end

PERFORMANCE displayHealthBar()

Health bar positions are recalculated and rendered every frame even when health hasn't changed

💡 Cache the previous health value and skip redrawing if health is unchanged; minor optimization but good practice

STYLE spiderman and deadpool objects

Objects use function shorthand syntax (display: function() {}) instead of modern arrow functions or method syntax

💡 Refactor to ES6 class syntax for cleaner inheritance and modern JavaScript style; would also reduce code duplication between characters

FEATURE deadpool.move()

AI does not jump or swing, making Spider-Man's movement abilities give unfair advantage

💡 Add simple jump or wall-climb logic to Deadpool when Spider-Man is significantly above ground level to create fairer combat

BUG keyPressed() and mousePressed()

Multiple overlapping key/mouse handlers with identical reset logic; pressing space during start screen triggers jump instead of resetting

💡 Consolidate input handlers to check gameState first, then branch to specific input handlers so state transitions happen before action processing

PERFORMANCE generateBuildings()

Buildings are regenerated on every window resize, even if the canvas size barely changed; creates temporary FPS drop

💡 Add a threshold check: only regenerate if canvas size changed by more than 50px to reduce unnecessary computation

🔄 Code Flow

Code flow showing setup, draw, spidermandisplay, spidermanmove, spidermanattack, deadpoolmove, generatebuildings, keyPressed, mousePressed, mouseReleased, resetGame, displayhealthbar, drawcitybackground, drawground, displaystartscreen, displaygameoverscreen

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

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Creation] setup --> perspective-setup[Perspective Setup] setup --> font-loading[Font Loading] setup --> resetGame[resetGame] setup --> draw[draw loop] click setup href "#fn-setup" click canvas-creation href "#sub-canvas-creation" click perspective-setup href "#sub-perspective-setup" click font-loading href "#sub-font-loading" click resetGame href "#fn-resetGame" draw --> game-state-check[Game State Check] draw --> camera-setup[Camera Setup] draw --> camera-rotation[Camera Rotation] draw --> health-check[Health Check] draw --> drawground[drawground] draw --> drawcitybackground[drawcitybackground] draw --> spidermandisplay[spidermandisplay] draw --> deadpoolmove[deadpoolmove] click draw href "#fn-draw" click game-state-check href "#sub-game-state-check" click camera-setup href "#sub-camera-setup" click camera-rotation href "#sub-camera-rotation" click health-check href "#sub-health-check" click drawground href "#fn-drawground" click drawcitybackground href "#fn-drawcitybackground" click spidermandisplay href "#fn-spidermandisplay" click deadpoolmove href "#fn-deadpoolmove" spidermandisplay --> body-sphere[Main Body] spidermandisplay --> eyes-loop[Eyes Positioning] spidermandisplay --> spider-symbol[Spider Symbol] spidermandisplay --> legs-loop[Four Legs] spidermandisplay --> web-line[Web Swing Line] click body-sphere href "#sub-body-sphere" click eyes-loop href "#sub-eyes-loop" click spider-symbol href "#sub-spider-symbol" click legs-loop href "#sub-legs-loop" click web-line href "#sub-web-line" spidermanmove[spidermanmove] --> keyboard-input[Keyboard Input] spidermanmove --> gravity-application[Gravity Application] spidermanmove --> ground-collision[Ground Collision] spidermanmove --> swing-physics[Web Swing Physics] spidermanmove --> distance-calculation[Distance Calculation] spidermanmove --> swing-acceleration[Swing Acceleration] spidermanmove --> friction-damping[Friction Damping] click spidermanmove href "#fn-spidermanmove" click keyboard-input href "#sub-keyboard-input" click gravity-application href "#sub-gravity-application" click ground-collision href "#sub-ground-collision" click swing-physics href "#sub-swing-physics" click distance-calculation href "#sub-distance-calculation" click swing-acceleration href "#sub-swing-acceleration" click friction-damping href "#sub-friction-damping" spidermanattack[spidermanattack] --> cooldown-check[Cooldown Check] spidermanattack --> range-check[Range Check] spidermanattack --> damage-application[Damage Application] spidermanattack --> attack-visual[Attack Visual] click spidermanattack href "#fn-spidermanattack" click cooldown-check href "#sub-cooldown-check" click range-check href "#sub-range-check" click damage-application href "#sub-damage-application" click attack-visual href "#sub-attack-visual" deadpoolmove --> ai-chase[AI Chase] deadpoolmove --> gravity-application[Gravity Application] deadpoolmove --> ground-collision[Ground Collision] deadpoolmove --> attack-trigger[Attack Trigger] click deadpoolmove href "#fn-deadpoolmove" click ai-chase href "#sub-ai-chase" click gravity-application href "#sub-gravity-application" click ground-collision href "#sub-ground-collision" click attack-trigger href "#sub-attack-trigger" generatebuildings[generateBuildings] --> array-init[Buildings Array Reset] generatebuildings --> x-loop[Horizontal Building Placement] generatebuildings --> z-loop[Depth Building Placement] generatebuildings --> building-object[Building Data] click generatebuildings href "#fn-generateBuildings" click array-init href "#sub-array-init" click x-loop href "#sub-x-loop" click z-loop href "#sub-z-loop" click building-object href "#sub-building-object" keyPressed[keyPressed] --> jump-trigger[Space Bar to Jump] keyPressed --> attack-trigger[E Key to Attack] keyPressed --> start-game[Any Key to Start] click keyPressed href "#fn-keyPressed" click jump-trigger href "#sub-jump-trigger" click attack-trigger href "#sub-attack-trigger" click start-game href "#sub-start-game" mousePressed[mousePressed] --> web-swing-start[Web Swing Initiation] mousePressed --> start-screen-click[Start Screen Click Handler] click mousePressed href "#fn-mousePressed" click web-swing-start href "#sub-web-swing-start" click start-screen-click href "#sub-start-screen-click" mouseReleased[mouseReleased] --> swing-end[Web Swing End] click mouseReleased href "#fn-mouseReleased" click swing-end href "#sub-swing-end" resetGame --> spiderman-reset[Spider-Man Reset] resetGame --> deadpool-reset[Deadpool Reset] resetGame --> camera-reset[Camera Reset] click resetGame href "#fn-resetGame" click spiderman-reset href "#sub-spiderman-reset" click deadpool-reset href "#sub-deadpool-reset" click camera-reset href "#sub-camera-reset" displayhealthbar[displayhealthbar] --> health-ratio-calc[Health Ratio] displayhealthbar --> background-bar[Dark Background Bar] displayhealthbar --> health-bar[Colored Health Bar] displayhealthbar --> text-label[Health Text] click displayhealthbar href "#fn-displayhealthbar" click health-ratio-calc href "#sub-health-ratio-calc" click background-bar href "#sub-background-bar" click health-bar href "#sub-health-bar" click text-label href "#sub-text-label" drawcitybackground --> building-loop[Building Drawing Loop] building-loop --> building-box[Building Box] click drawcitybackground href "#fn-drawcitybackground" click building-loop href "#sub-building-loop" click building-box href "#sub-building-box" drawground --> ground-plane[Ground Plane] click drawground href "#fn-drawground" click ground-plane href "#sub-ground-plane" displaystartscreen[displaystartscreen] --> start-screen-panel[Background Panel] displaystartscreen --> start-text[Text Display] displaystartscreen --> fallback-boxes[Fallback Boxes] click displaystartscreen href "#fn-displaystartscreen" click start-screen-panel href "#sub-start-screen-panel" click start-text href "#sub-start-text" click fallback-boxes href "#sub-fallback-boxes" displaygameoverscreen[displaygameoverscreen] --> gameover-panel[Background Panel] displaygameoverscreen --> winner-check[Winner Determination] displaygameoverscreen --> gameover-text[Game Over Text] click displaygameoverscreen href "#fn-displaygameoverscreen" click gameover-panel href "#sub-gameover-panel" click winner-check href "#sub-winner-check" click gameover-text href "#sub-gameover-text"

❓ Frequently Asked Questions

What visual elements are featured in the lit fun p5.js sketch?

The sketch showcases a 3D environment featuring characters like Spider-Man and Deadpool, with vibrant colors and dynamic movements as they interact within the game space.

How can users interact with the lit fun sketch?

Users can control Spider-Man's movements, including jumping and swinging, while navigating through the game environment and engaging with Deadpool.

What creative coding techniques are demonstrated in the lit fun sketch?

This sketch demonstrates 3D rendering techniques in p5.js, including camera manipulation and character physics, to create an engaging interactive experience.

Preview

lit fun - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of lit fun - Code flow showing setup, draw, spidermandisplay, spidermanmove, spidermanattack, deadpoolmove, generatebuildings, keyPressed, mousePressed, mouseReleased, resetGame, displayhealthbar, drawcitybackground, drawground, displaystartscreen, displaygameoverscreen
Code Flow Diagram