spider man 2 test

This sketch creates a 3D Spider-Man vs Venom battle scene in WEBGL mode with a procedurally generated city, realistic lighting, and character models built from geometric shapes. Miles Morales and Venom start on opposite sides of the canvas, approach each other, fight with visual effects, and reset to repeat the cycle.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make Miles start closer to Venom — Change the initial X positions to make the approach phase shorter - they'll start closer together
  2. Speed up the approach animations — Characters will walk toward each other twice as fast, making the fight sequence more dynamic
  3. Generate a denser city with more buildings — Changes the building count to 60, creating a much more crowded urban environment
  4. Paint all buildings bright red — Every building turns red instead of gray - creates a dramatic sci-fi atmosphere
  5. Make all building windows glow brightly — Changes the random window threshold so 90% of windows are lit instead of 70%, making the city feel alive
  6. Create colorful fight effects — Adds multiple impact spheres in different colors during the fight phase for a more vibrant battle
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a fully 3D battle scene using p5.js WEBGL mode, where Miles Morales and Venom fight in a procedurally generated city. The sketch demonstrates sophisticated p5.js techniques including WEBGL transforms (translate, rotate, scale), realistic 3D lighting with multiple light sources, interactive camera control via orbitControl(), procedural generation of buildings with windows, and character models constructed entirely from geometric primitives like boxes, spheres, and cylinders.

The code is organized into setup(), which initializes the 3D scene and generates building data once, and draw(), which runs 60 times per second to update character positions, render the city, apply lighting, and animate the fight sequence. By studying this sketch, you will learn how to structure a complex 3D animation using state machines (fightState), how to generate procedural environments, how to build detailed characters from primitives, and how to layer multiple light sources for cinematic depth.

⚙️ How It Works

  1. When the sketch loads, setup() creates a WEBGL canvas at full window dimensions, positions Miles Morales on the left (x = -200) and Venom on the right (x = 200), and generates 30 buildings with random heights and positions scattered around the scene using the generateBuildings() function.
  2. Every frame, draw() clears the background with a sky color and applies three directional light sources from different angles to create realistic shading on all 3D objects.
  3. The camera is set to orbit around the scene center using orbitControl(), allowing mouse interaction to rotate and zoom around the battle.
  4. The ground plane and roads are drawn as a large green surface with darker gray road lanes and white lane markers using rotated planes.
  5. Buildings from the stored array are drawn at their stored positions with randomly lit windows on all four faces, creating depth and a populated cityscape.
  6. Miles Morales and Venom are drawn at their current positions using custom drawing functions (drawMilesMorales and drawVenom) that build detailed characters from boxes, spheres, cylinders, and spider/venom symbols.
  7. The handleFightState() function manages a three-phase sequence: Miles approaches Venom (fightState 0), Venom approaches Miles (fightState 1), and both engage in combat with red impact spheres and random colored lines (fightState 2), before resetting every 100 frames.

🎓 Concepts You'll Learn

WEBGL 3D rendering3D transforms (translate, rotate, scale)Procedural generationState machinesLighting and shadingInteractive camera controlCharacter modeling with primitivesAnimation loops

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. In WEBGL sketches, it's where you create your canvas in 3D mode, set the camera position, and pre-generate static data like the city buildings that never change.

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  milesX = -200; // Start Miles on the left
  milesZ = random(-100, 100); // Randomize Z slightly
  venomX = 200; // Start Venom on the right
  venomZ = random(-100, 100); // Randomize Z slightly
  
  // Set initial camera position for a better view of the city
  camera(0, -300, 500, 0, 0, 0, 0, 1, 0);

  // Generate buildings once in setup()
  generateBuildings();
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

function-call WEBGL Canvas Setup createCanvas(windowWidth, windowHeight, WEBGL);

Creates a full-window 3D rendering canvas using WEBGL for hardware-accelerated 3D graphics

assignment Character Initial Positions milesX = -200; milesZ = random(-100, 100); venomX = 200; venomZ = random(-100, 100);

Places Miles Morales on the left and Venom on the right with randomized Z depth for variety each restart

function-call Camera Positioning camera(0, -300, 500, 0, 0, 0, 0, 1, 0);

Sets the initial 3D camera position above and behind the scene looking toward the center (0, 0, 0)

function-call Procedural Building Generation generateBuildings();

Creates all 30 buildings once at startup with random heights and positions - efficient since buildings never move

createCanvas(windowWidth, windowHeight, WEBGL);
Creates a WEBGL canvas that fills the entire window - WEBGL is p5.js's 3D rendering mode using your GPU
milesX = -200;
Sets Miles Morales' starting X position 200 units to the left of the scene center
milesZ = random(-100, 100);
Randomizes Miles' depth (Z position) between -100 and 100 so he doesn't start in exactly the same spot each cycle
venomX = 200;
Sets Venom's starting X position 200 units to the right of the scene center
venomZ = random(-100, 100);
Randomizes Venom's depth so each fight cycle feels slightly different
camera(0, -300, 500, 0, 0, 0, 0, 1, 0);
Positions the camera at (0, -300, 500) looking toward (0, 0, 0) - the first three numbers are camera position, the next three are the look-at target, the last three define which way is 'up' (0, 1, 0)
generateBuildings();
Calls the building generation function to populate the buildings array once at startup

draw()

draw() executes 60 times per second in p5.js. In this 3D scene, it clears the canvas, applies lighting to simulate a real environment, handles camera interaction, draws all static geometry (ground, buildings), draws the animated characters, and updates the fight logic every single frame.

🔬 These four lines create cinematic 3-point lighting. What happens if you change the first number of the second directionalLight from 255 to 0, making the top light invisible?

  ambientLight(100); // Soft ambient light
  directionalLight(255, 255, 255, 0, -1, 0); // Light from above
  directionalLight(150, 150, 150, -0.5, -0.5, 0.5); // Light from front-left
  directionalLight(100, 100, 100, 0.5, -0.2, -0.5); // Light from back-right

🔬 This block draws Miles. What happens if you change rotateY(HALF_PI) to rotateY(0) so Miles doesn't rotate at all?

  push();
  translate(milesX, 0, milesZ);
  rotateY(HALF_PI); // Face forward or towards venom
  drawMilesMorales();
  pop();
function draw() {
  background(100, 150, 200); // Sky color
  
  // Basic lighting
  ambientLight(100); // Soft ambient light
  directionalLight(255, 255, 255, 0, -1, 0); // Light from above
  directionalLight(150, 150, 150, -0.5, -0.5, 0.5); // Light from front-left
  directionalLight(100, 100, 100, 0.5, -0.2, -0.5); // Light from back-right

  // Camera control for "free roam"
  // Move the camera around with the mouse
  orbitControl(); 

  // Ground plane and roads
  drawGroundAndRoads();

  // Draw Buildings from the stored data
  drawBuildings();

  // Miles Morales
  push();
  translate(milesX, 0, milesZ);
  rotateY(HALF_PI); // Face forward or towards venom
  drawMilesMorales();
  pop();

  // Venom
  push();
  translate(venomX, 0, venomZ);
  rotateY(-HALF_PI); // Face forward or towards miles
  drawVenom();
  pop();

  // Basic fight animation
  handleFightState();
}
Line-by-line explanation (13 lines)

🔧 Subcomponents:

function-call Three-Point Lighting ambientLight(100); directionalLight(255, 255, 255, 0, -1, 0); directionalLight(150, 150, 150, -0.5, -0.5, 0.5); directionalLight(100, 100, 100, 0.5, -0.2, -0.5);

Creates cinematic lighting with ambient light, a bright top light, and two fill lights from different angles for depth

function-call Interactive Camera orbitControl();

Lets the viewer rotate and zoom the camera with the mouse while the scene continues animating

push-pop-block Miles Morales Rendering push(); translate(milesX, 0, milesZ); rotateY(HALF_PI); drawMilesMorales(); pop();

Positions Miles at his current coordinates, rotates him to face forward, and draws his detailed model

push-pop-block Venom Rendering push(); translate(venomX, 0, venomZ); rotateY(-HALF_PI); drawVenom(); pop();

Positions Venom at his current coordinates, rotates him to face toward Miles, and draws his model

function-call Fight State Manager handleFightState();

Updates character positions based on fight phase and draws battle effects

background(100, 150, 200);
Clears the canvas with sky blue (light, not too bright) to provide a pleasant backdrop
ambientLight(100);
Adds overall ambient light so shadowed areas aren't pitch black - value of 100 means moderate ambient brightness
directionalLight(255, 255, 255, 0, -1, 0);
Creates a white directional light from above (direction 0, -1, 0 = straight down) simulating the sun
directionalLight(150, 150, 150, -0.5, -0.5, 0.5);
Adds a secondary light from the front-left to fill shadows and add depth
directionalLight(100, 100, 100, 0.5, -0.2, -0.5);
Adds a third light from the back-right to create separation between characters and background
orbitControl();
Enables mouse-controlled camera orbiting - drag to rotate, scroll to zoom around the scene center
drawGroundAndRoads();
Draws the green ground plane and gray roads with lane markers
drawBuildings();
Loops through the buildings array and draws each building with its stored dimensions and random window lights
translate(milesX, 0, milesZ);
Moves the 3D drawing origin to Miles' current position before drawing him
rotateY(HALF_PI);
Rotates Miles 90 degrees around the Y axis so he faces toward Venom
translate(venomX, 0, venomZ);
Moves the drawing origin to Venom's current position
rotateY(-HALF_PI);
Rotates Venom -90 degrees so he faces toward Miles (opposite direction)
handleFightState();
Runs the fight logic that updates positions and draws collision effects

drawMilesMorales()

This function constructs Miles Morales entirely from geometric primitives (boxes, spheres, cylinders, and lines). It demonstrates how to build complex 3D models by combining simple shapes with push/pop transforms to position each part independently. The function is called from draw() to render Miles at his current position.

🔬 This section builds the spider body and head from two boxes. What if you change the second box from (6, 6, 2) to (10, 10, 2)? What does the spider symbol look like now?

  // Spider Symbol (simplified with small red boxes)
  push();
  translate(0, -10, 11.5); // Even further out
  // Body
  box(10, 10, 2);
  // Head
  push();
  translate(0, -10, 0);
  box(6, 6, 2);
  pop();
function drawMilesMorales() {
  // Suit (black with red accents)
  fill(0);
  noStroke();
  box(30, 60, 20); // Body

  // Red Accents
  fill(180, 0, 0); // Darker red
  push();
  translate(0, 25, 0); // Waist
  box(30, 10, 20);
  pop();
  push();
  translate(0, -25, 0); // Shoulders
  box(30, 10, 20);
  pop();

  // Red Spider-Man symbol area on chest (more defined)
  fill(200, 0, 0); // Red
  push();
  translate(0, -10, 10.5); // Slightly out from chest
  box(20, 20, 2); // Main red square
  pop();
  
  // Spider Symbol (simplified with small red boxes)
  push();
  translate(0, -10, 11.5); // Even further out
  // Body
  box(10, 10, 2);
  // Head
  push();
  translate(0, -10, 0);
  box(6, 6, 2);
  pop();
  // Legs (simplified)
  push();
  translate(-7, -4, 0);
  rotateZ(HALF_PI/4);
  box(14, 2, 2);
  pop();
  push();
  translate(7, -4, 0);
  rotateZ(-HALF_PI/4);
  box(14, 2, 2);
  pop();
  push();
  translate(-7, 4, 0);
  rotateZ(-HALF_PI/4);
  box(14, 2, 2);
  pop();
  push();
  translate(7, 4, 0);
  rotateZ(HALF_PI/4);
  box(14, 2, 2);
  pop();
  pop();

  // Head (black)
  push();
  translate(0, -40, 0);
  fill(0);
  sphere(15);
  // Eyes (white)
  fill(255);
  push();
  translate(-5, -5, 14); // Left eye
  rotateZ(HALF_PI/12);
  ellipse(0, 0, 8, 16);
  pop();
  push();
  translate(5, -5, 14); // Right eye
  rotateZ(-HALF_PI/12);
  ellipse(0, 0, 8, 16);
  pop();
  pop();

  // Legs (black)
  fill(0);
  push();
  translate(-5, 30, 0);
  box(10, 30, 15);
  pop();
  push();
  translate(5, 30, 0);
  box(10, 30, 15);
  pop();

  // Arms (black, simple cylinders)
  fill(0);
  push();
  translate(-20, -10, 0);
  cylinder(5, 40);
  pop();
  push();
  translate(20, -10, 0);
  cylinder(5, 40);
  pop();

  // Web Lines (symbolic, on body)
  stroke(100, 0, 0); // Dark red web lines
  strokeWeight(0.5);
  push();
  translate(0, 0, 10.5); // On front face of body
  // Vertical
  line(-10, -25, 0, -10, 25, 0);
  line(0, -25, 0, 0, 25, 0);
  line(10, -25, 0, 10, 25, 0);
  // Horizontal (approximate)
  line(-15, -15, 0, 15, -15, 0);
  line(-15, 0, 0, 15, 0, 0);
  line(-15, 15, 0, 15, 15, 0);
  pop();
}
Line-by-line explanation (13 lines)

🔧 Subcomponents:

shape Main Body box(30, 60, 20); // Body

Creates the central torso of Miles as a black rectangular box

shape-group Red Suit Accents fill(180, 0, 0); ... box(30, 10, 20); ... box(30, 10, 20);

Adds waist and shoulder red accent bands to break up the black and create visual interest

shape-group Spider Symbol on Chest push(); translate(0, -10, 10.5); box(20, 20, 2); ... pop();

Draws a stylized spider symbol using boxes on the chest to identify the character as Spider-Man

shape Head with Eyes sphere(15); ... ellipse(0, 0, 8, 16);

Creates a black spherical head with two white oval eye shapes

shape-group Legs and Arms push(); translate(-5, 30, 0); box(10, 30, 15); pop();

Builds realistic limbs using boxes for legs and cylinders for arms

line-group Web Pattern Lines line(-10, -25, 0, -10, 25, 0);

Draws dark red web lines on the body to add visual detail and reinforce the Spider-Man theme

fill(0);
Sets the fill color to black (0 in RGB) for the main suit
box(30, 60, 20);
Draws the main torso as a box: 30 units wide, 60 tall, 20 deep
fill(180, 0, 0);
Changes fill to a darker red (180 red, 0 green, 0 blue) for accent stripes
translate(0, 25, 0);
Moves the drawing position 25 units up (Y+) to place the waist accent above the body center
translate(0, -10, 10.5);
Moves to the upper chest area and slightly forward (Z+) to place the spider symbol in front
box(20, 20, 2);
Draws a flat red square (2 units thick) for the spider symbol base
translate(0, -40, 0);
Moves 40 units up to position the head above the body
sphere(15);
Draws a sphere with radius 15 for the head
fill(255);
Changes to white for the eye shapes
ellipse(0, 0, 8, 16);
Draws a stretched oval (8 wide, 16 tall) for an eye - the 3D version appears as a flat shape on the sphere
cylinder(5, 40);
Draws an arm as a cylinder with radius 5 and height 40
stroke(100, 0, 0);
Sets line color to dark red for the web pattern
line(-10, -25, 0, -10, 25, 0);
Draws a vertical line from point (-10, -25, 0) to (-10, 25, 0) to create part of the web grid

drawVenom()

This function builds Venom as the contrast to Miles: larger body, angular cubic head instead of a sphere, white eyes and symbol on black (inverted from Miles), and radiating tendrils to emphasize the alien symbiote nature. The tendril loop demonstrates how map() can convert a loop counter into circular angles, a common technique for drawing radial patterns.

🔬 This loop draws 8 tendrils radiating from Venom. What if you change the 8 to 16? How does the symbiote look with twice as many tentacles?

  for (let i = 0; i < 8; i++) {
    let angle = map(i, 0, 8, 0, TWO_PI);
    let startX = cos(angle) * 15;
    let startY = sin(angle) * 20;
    let endX = cos(angle) * random(20, 40);
    let endY = sin(angle) * random(25, 50);
    line(startX, startY, 0, endX, endY, 0);
  }
function drawVenom() {
  // Body (larger, black)
  fill(0);
  noStroke();
  box(50, 90, 30);

  // White Spider symbol on chest
  fill(255); // White
  push();
  translate(0, -10, 15.5); // Front of chest, slightly out
  box(30, 40, 2); // Main white box
  // Spider Symbol (simplified with small white boxes)
  push();
  translate(0, -10, 1.5); // Even further out
  // Body
  box(15, 15, 2);
  // Head
  push();
  translate(0, -15, 0);
  box(9, 9, 2);
  pop();
  // Legs (simplified)
  push();
  translate(-10.5, -6, 0);
  rotateZ(HALF_PI/4);
  box(21, 3, 2);
  pop();
  push();
  translate(10.5, -6, 0);
  rotateZ(-HALF_PI/4);
  box(21, 3, 2);
  pop();
  push();
  translate(-10.5, 6, 0);
  rotateZ(-HALF_PI/4);
  box(21, 3, 2);
  pop();
  push();
  translate(10.5, 6, 0);
  rotateZ(HALF_PI/4);
  box(21, 3, 2);
  pop();
  pop();
  pop();

  // Head (larger, black, angular)
  fill(0);
  push();
  translate(0, -60, 0);
  box(35, 35, 35); // Angular head
  // Eyes (white, more angular)
  fill(255);
  push();
  translate(-12, -7, 18);
  beginShape();
  vertex(-5, -10, 0);
  vertex(5, -10, 0);
  vertex(10, 5, 0);
  vertex(-10, 5, 0);
  endShape(CLOSE);
  pop();
  push();
  translate(12, -7, 18);
  beginShape();
  vertex(-5, -10, 0);
  vertex(5, -10, 0);
  vertex(10, 5, 0);
  vertex(-10, 5, 0);
  endShape(CLOSE);
  pop();
  // Tongue (red)
  fill(200, 0, 0);
  push();
  translate(0, 10, 18);
  rotateX(HALF_PI/4);
  cylinder(5, 30);
  pop();
  pop();

  // Legs (larger, black)
  fill(0);
  push();
  translate(-10, 45, 0);
  box(15, 40, 20);
  pop();
  push();
  translate(10, 45, 0);
  box(15, 40, 20);
  pop();

  // Arms (larger, black)
  fill(0);
  push();
  translate(-30, -20, 0);
  box(10, 60, 20);
  pop();
  push();
  translate(30, -20, 0);
  box(10, 60, 20);
  pop();

  // Tendrils (symbolic, emanating from body)
  stroke(50); // Dark gray tendrils
  strokeWeight(1);
  push();
  translate(0, 0, 15.5); // On front face of body
  for (let i = 0; i < 8; i++) {
    let angle = map(i, 0, 8, 0, TWO_PI);
    let startX = cos(angle) * 15;
    let startY = sin(angle) * 20;
    let endX = cos(angle) * random(20, 40);
    let endY = sin(angle) * random(25, 50);
    line(startX, startY, 0, endX, endY, 0);
  }
  pop();
}
Line-by-line explanation (13 lines)

🔧 Subcomponents:

shape Larger Torso box(50, 90, 30);

Creates Venom's main body as a larger box than Miles to show his larger size

shape-group White Spider Symbol fill(255); ... box(30, 40, 2); ...

Draws a white spider symbol on black body to identify Venom as a symbiote

shape Angular Head with Eyes and Tongue box(35, 35, 35); ... beginShape(); ... cylinder(5, 30);

Creates a distinctive cubic head with white eyes and red tongue to make Venom intimidating and recognizable

shape-group Larger Arms and Legs box(15, 40, 20); ... box(10, 60, 20);

Larger limbs than Miles to emphasize Venom's superior size and strength

line-loop Symbiote Tendrils for (let i = 0; i < 8; i++) { ... line(startX, startY, 0, endX, endY, 0); }

Draws 8 radiating lines from Venom's body to simulate the alien symbiote's tentacle-like nature

box(50, 90, 30);
Draws Venom's main body: 50 wide, 90 tall, 30 deep - larger than Miles' 30x60x20 box
fill(255);
Sets fill to white for Venom's spider symbol (inverted from Miles' red)
translate(0, -10, 15.5);
Positions the white symbol on the front of Venom's chest
box(35, 35, 35);
Creates a cubic head (35x35x35) that looks angular and menacing compared to Miles' round head
beginShape();
Starts defining a custom polygon shape for Venom's eye (more angular than a simple ellipse)
vertex(-5, -10, 0);
Adds a corner point of the eye polygon at (-5, -10, 0)
endShape(CLOSE);
Finishes the polygon and closes it back to the first vertex
fill(200, 0, 0);
Changes color to red for the tongue
rotateX(HALF_PI/4);
Tilts the tongue forward 22.5 degrees to make it protrude menacingly
cylinder(5, 30);
Draws the tongue as a cylinder: radius 5, height 30
let angle = map(i, 0, 8, 0, TWO_PI);
Maps the loop counter (0-8) to angles around a circle (0 to 2π) - distributes tendrils evenly in a circle
let startX = cos(angle) * 15;
Calculates the X coordinate where each tendril starts, 15 units from center at the current angle
line(startX, startY, 0, endX, endY, 0);
Draws a line from the calculated start point to a randomized end point to create a tendril effect

drawGroundAndRoads()

This function creates the urban environment base: a large green ground plane with a grid of intersecting roads and lane markers. It demonstrates how to use loops to create repeated geometry efficiently - rather than manually drawing 5 horizontal and 5 vertical roads, the loops generate them programmatically.

🔬 This loop draws horizontal roads spaced 200 units apart (spacing variable). What if you change spacing from 200 to 100? How does the road grid become denser?

  // Horizontal roads
  for (let i = -floor(numRoads/2); i <= floor(numRoads/2); i++) {
    push();
    translate(0, i * spacing, 1); // Lift slightly above ground
    plane(1000, roadWidth);
function drawGroundAndRoads() {
  push();
  translate(0, 100, 0); // Move ground down
  rotateX(HALF_PI);
  noStroke();

  // Ground plane
  fill(50, 100, 50); // Green ground
  plane(1000, 1000); // Large ground plane

  // Roads
  fill(40); // Dark gray for roads
  let roadWidth = 80;
  let numRoads = 5;
  let spacing = 200;

  // Horizontal roads
  for (let i = -floor(numRoads/2); i <= floor(numRoads/2); i++) {
    push();
    translate(0, i * spacing, 1); // Lift slightly above ground
    plane(1000, roadWidth);
    // Lane markers (white)
    fill(255, 200);
    for (let j = -floor(1000/100); j <= floor(1000/100); j++) {
      if (j !== 0) plane(20, 5, 2); // Dashed lines
    }
    pop();
  }

  // Vertical roads
  for (let i = -floor(numRoads/2); i <= floor(numRoads/2); i++) {
    push();
    translate(i * spacing, 0, 1); // Lift slightly above ground
    rotateZ(HALF_PI); // Rotate to be vertical
    plane(1000, roadWidth);
    // Lane markers (white)
    fill(255, 200);
    for (let j = -floor(1000/100); j <= floor(1000/100); j++) {
      if (j !== 0) plane(20, 5, 2); // Dashed lines
    }
    pop();
  }

  pop();
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

transform Ground Setup Transform translate(0, 100, 0); rotateX(HALF_PI);

Moves the ground down 100 units and rotates it 90 degrees so the plane lies flat

shape Main Ground Plane plane(1000, 1000);

Creates a large 1000x1000 unit green ground surface

for-loop Horizontal Road Loop for (let i = -floor(numRoads/2); i <= floor(numRoads/2); i++) { ... }

Draws 5 horizontal roads spaced 200 units apart with lane markers

for-loop Vertical Road Loop for (let i = -floor(numRoads/2); i <= floor(numRoads/2); i++) { ... rotateZ(HALF_PI); ... }

Draws 5 vertical roads perpendicular to horizontal roads with lane markers

translate(0, 100, 0);
Moves the ground down 100 units on the Y axis so it appears below the characters
rotateX(HALF_PI);
Rotates 90 degrees around the X axis so the plane lies flat (by default plane() is vertical)
plane(1000, 1000);
Draws a square plane 1000x1000 units - large enough to contain all the city elements
fill(40);
Sets color to dark gray (40, 40, 40) for the road surfaces
let roadWidth = 80;
Road width is 80 units, wide enough for the 1000-unit plane to fit 80-unit roads across it
let numRoads = 5;
Creates 5 roads total (numRoads/2 = 2.5, floored to 2, so -2 to 2 = 5 roads)
let spacing = 200;
Roads are 200 units apart, creating an evenly spaced grid pattern
for (let i = -floor(numRoads/2); i <= floor(numRoads/2); i++) {
Loop from -2 to 2 (when numRoads=5), creating 5 iterations for 5 roads
translate(0, i * spacing, 1);
Positions each road at intervals of 200 units, offset by 1 unit up (Z+) to appear above ground
plane(1000, roadWidth);
Draws a single road as a plane 1000 units long and 80 units wide
rotateZ(HALF_PI);
Rotates the vertical roads 90 degrees so they cross the horizontal roads perpendicularly
if (j !== 0) plane(20, 5, 2);
Draws lane markers (white dashes) along each road, skipping the center to avoid overlap

generateBuildings()

This function demonstrates procedural generation: creating diverse, interesting content programmatically rather than manually placing each object. The do-while loop shows a practical pattern for trying to place objects with constraints (spacing), and storing data as JavaScript objects in an array allows draw() to render many buildings with a simple loop.

🔬 This do-while loop places buildings in a circular pattern around the origin using cos/sin with random distance. What if you remove the angle calculation and just use x = random(-cityRadius, cityRadius) instead? How does the city shape change?

    do {
      let angle = random(TWO_PI);
      x = cos(angle) * random(100, cityRadius) + random(-50, 50); // Randomize position
      z = sin(angle) * random(100, cityRadius) + random(-50, 50);
      attempts++;
    } while (attempts < 100 && isBuildingOverlapping(x, z, buildingSpacing));
function generateBuildings() {
  let numBuildings = 30; // More buildings
  let cityRadius = 500; // Larger city area
  let buildingSpacing = 80; // Minimum distance between buildings

  for (let i = 0; i < numBuildings; i++) {
    let x, z;
    // Try to place buildings without too much overlap
    let attempts = 0;
    do {
      let angle = random(TWO_PI);
      x = cos(angle) * random(100, cityRadius) + random(-50, 50); // Randomize position
      z = sin(angle) * random(100, cityRadius) + random(-50, 50);
      attempts++;
    } while (attempts < 100 && isBuildingOverlapping(x, z, buildingSpacing));

    let h = random(80, 300); // Random height
    let w = random(30, 100); // Random width/depth (can be non-square)
    let d = random(30, 100);

    buildings.push({ x: x, z: z, h: h, w: w, d: d });
  }
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

assignment Generation Parameters let numBuildings = 30; let cityRadius = 500; let buildingSpacing = 80;

Sets up the rules for how many buildings to create and where they can be placed

for-loop Building Creation Loop for (let i = 0; i < numBuildings; i++) { ... }

Iterates 30 times to create 30 building objects with unique properties

do-while-loop Collision Avoidance do { ... } while (attempts < 100 && isBuildingOverlapping(x, z, buildingSpacing));

Attempts up to 100 times to place a building in a location without overlap

assignment Building Randomization let h = random(80, 300); let w = random(30, 100); let d = random(30, 100);

Generates random dimensions so each building is unique

function-call Building Storage buildings.push({ x: x, z: z, h: h, w: w, d: d });

Stores the building data in the global buildings array for later drawing

let numBuildings = 30;
Sets how many buildings to generate - 30 provides good city density without slowing performance too much
let cityRadius = 500;
Buildings can be randomly placed anywhere within 500 units from the origin, creating a manageable city area
let buildingSpacing = 80;
Minimum distance between buildings - prevents them from overlapping too much (though the overlap check is simplified)
for (let i = 0; i < numBuildings; i++) {
Loop 30 times to create 30 buildings
let angle = random(TWO_PI);
Generates a random angle (0 to 2π) to create circular distribution around the origin
x = cos(angle) * random(100, cityRadius) + random(-50, 50);
Calculates X position using cos(angle) and a random distance, then adds random jitter to avoid perfect circles
z = sin(angle) * random(100, cityRadius) + random(-50, 50);
Calculates Z position using sin(angle) - combining angle and distance creates radial distribution
attempts++;
Increments the attempt counter - if 100 attempts are made, the placement succeeds anyway
while (attempts < 100 && isBuildingOverlapping(x, z, buildingSpacing));
Keeps trying to place the building if it overlaps and fewer than 100 attempts have been made
let h = random(80, 300);
Random height between 80 and 300 units - tall buildings look impressive
let w = random(30, 100);
Random width between 30 and 100 units - creates varied building shapes
buildings.push({ x: x, z: z, h: h, w: w, d: d });
Creates a new JavaScript object with all building properties and adds it to the buildings array

drawBuildings()

This function is the workhorse of the visual scene - it renders every building with detail. For each building, it draws a main box and then overlays randomized windows on all four faces using nested loops. This demonstrates key 3D techniques: using translate() to position on each face, using rotateY() to orient faces correctly, and using nested loops to create regular grids efficiently.

🔬 This nested loop draws a grid of windows on each face. The inner loop moves right by 'windowSize + windowGap' each iteration. What if you changed this to just windowSize (no gap)? What happens to the window spacing?

    for (let y = startY; y < endY; y += windowSize + windowGap) {
      for (let x_w = startX_w; x_w < endX_w; x_w += windowSize + windowGap) {
        if (random(1) > 0.3) plane(windowSize, windowSize); // Randomly lit windows
        translate(windowSize + windowGap, 0, 0);
      }
function drawBuildings() {
  for (let building of buildings) {
    let x = building.x;
    let z = building.z;
    let h = building.h;
    let w = building.w;
    let d = building.d;

    push();
    translate(x, 0 - h / 2, z); // Position at the base of the building
    fill(100, 100, 100); // Darker gray buildings
    box(w, h, d);

    // Add windows
    fill(255, 255, 200, 200); // Slightly yellow, semi-transparent windows
    let windowGap = 10;
    let windowSize = 8;
    let startY = -h / 2 + windowGap;
    let endY = h / 2 - windowGap;
    let startX_w = -w / 2 + windowGap;
    let endX_w = w / 2 - windowGap;
    let startX_d = -d / 2 + windowGap;
    let endX_d = d / 2 - windowGap;

    // Front face (Z+)
    push();
    translate(0, 0, d/2 + 1); // Move slightly out
    for (let y = startY; y < endY; y += windowSize + windowGap) {
      for (let x_w = startX_w; x_w < endX_w; x_w += windowSize + windowGap) {
        if (random(1) > 0.3) plane(windowSize, windowSize); // Randomly lit windows
        translate(windowSize + windowGap, 0, 0);
      }
      translate(-(endX_w - startX_w + windowGap), windowSize + windowGap, 0);
    }
    pop();

    // Back face (Z-)
    push();
    translate(0, 0, -d/2 - 1); // Move slightly out
    for (let y = startY; y < endY; y += windowSize + windowGap) {
      for (let x_w = startX_w; x_w < endX_w; x_w += windowSize + windowGap) {
        if (random(1) > 0.3) plane(windowSize, windowSize);
        translate(windowSize + windowGap, 0, 0);
      }
      translate(-(endX_w - startX_w + windowGap), windowSize + windowGap, 0);
    }
    pop();

    // Right face (X+)
    push();
    translate(w/2 + 1, 0, 0); // Move slightly out
    rotateY(HALF_PI);
    for (let y = startY; y < endY; y += windowSize + windowGap) {
      for (let x_d = startX_d; x_d < endX_d; x_d += windowSize + windowGap) {
        if (random(1) > 0.3) plane(windowSize, windowSize);
        translate(windowSize + windowGap, 0, 0);
      }
      translate(-(endX_d - startX_d + windowGap), windowSize + windowGap, 0);
    }
    pop();

    // Left face (X-)
    push();
    translate(-w/2 - 1, 0, 0); // Move slightly out
    rotateY(-HALF_PI);
    for (let y = startY; y < endY; y += windowSize + windowGap) {
      for (let x_d = startX_d; x_d < endX_d; x_d += windowSize + windowGap) {
        if (random(1) > 0.3) plane(windowSize, windowSize);
        translate(windowSize + windowGap, 0, 0);
      }
      translate(-(endX_d - startX_d + windowGap), windowSize + windowGap, 0);
    }
    pop();

    pop();
  }
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

for-loop Buildings Array Loop for (let building of buildings) { ... }

Iterates through each building object stored during generation

shape Building Main Box translate(x, 0 - h / 2, z); ... box(w, h, d);

Positions and draws the main building structure at the stored coordinates

assignment Window Layout Calculations let startY = -h / 2 + windowGap; ... let startX_d = -d / 2 + windowGap;

Pre-calculates window grid bounds on each face to avoid loops recalculating every frame

nested-loop Front Face Windows for (let y = startY; y < endY; y += windowSize + windowGap) { for (let x_w = startX_w; x_w < endX_w; x_w += windowSize + windowGap) { ... } }

Draws a grid of randomly lit windows on the front face

nested-loop Side Face Windows rotateY(HALF_PI); for (let y = startY; y < endY; y += windowSize + windowGap) { ... }

Draws windowed grids on right and left faces using rotateY to orient the faces correctly

for (let building of buildings) {
Uses for-of loop to iterate through each building object in the buildings array - cleaner than traditional index-based loops
let x = building.x;
Extracts the X position from the building object for easier reference
translate(x, 0 - h / 2, z);
Moves to the building's location and offsets Y by -h/2 so the building sits on the ground rather than being buried in it
fill(100, 100, 100);
Sets building color to medium gray (100, 100, 100)
box(w, h, d);
Draws the main building structure with its unique width, height, and depth
fill(255, 255, 200, 200);
Sets window color to soft yellow with 200 alpha (semi-transparent) to look like lit windows
let windowGap = 10;
Space between windows is 10 units - controls density of window grid
let startY = -h / 2 + windowGap;
Windows start 10 units below the building top to leave margin
translate(0, 0, d/2 + 1);
Moves to the front face of the building, offset 1 unit forward so windows appear on the surface
for (let y = startY; y < endY; y += windowSize + windowGap) {
Outer loop iterates down the building height, stepping by window size + gap
for (let x_w = startX_w; x_w < endX_w; x_w += windowSize + windowGap) {
Inner loop iterates across the building width at each row
if (random(1) > 0.3) plane(windowSize, windowSize);
70% of the time, draws a window at this grid position (creates the effect of some lights being on/off)
translate(windowSize + windowGap, 0, 0);
Moves right to the next window position in the grid
rotateY(HALF_PI);
Rotates 90 degrees so the next window loop draws on a perpendicular face

isBuildingOverlapping()

This function is currently a stub - it always returns false, allowing all buildings to be placed. In a complete version, it would check the distance from the new building to all previously placed buildings and return true if they're too close together. This is left as a learning opportunity: students can implement the full collision detection algorithm here.

function isBuildingOverlapping(newX, newZ, minDist) {
  // This would need to store building positions and dimensions for accurate checking
  // For simplicity, we'll just return false for now to allow placement
  return false;
}
Line-by-line explanation (2 lines)
function isBuildingOverlapping(newX, newZ, minDist) {
Function receives a new building position (newX, newZ) and a minimum allowed distance (minDist) for collision checking
return false;
Always returns false, meaning buildings are always allowed to be placed regardless of overlap - a simplified placeholder

handleFightState()

This function implements a simple state machine - a fundamental pattern in game and animation development. The three states (approach, approach, fight) demonstrate how to break complex behavior into discrete phases that transition when conditions are met. frameCount % 100 is a common technique for triggering events at regular intervals: frameCount increments every frame, so % 100 returns 0 every 100 frames.

🔬 This is the state machine - each if/else block is a state. States transition when distance thresholds are met. What if you changed the '50' in both distance checks to '100'? How much farther apart do the characters have to be before they engage?

  if (fightState === 0) {
    // Miles Morales approaches Venom
    if (milesX < venomX - 50) {
      milesX += 1;
    } else {
      fightState = 1; // Miles is close, now Venom approaches
    }
  } else if (fightState === 1) {
    // Venom approaches Miles
    if (venomX > milesX + 50) {
      venomX -= 1;
    } else {
      fightState = 2; // Both are close, start the "fight"
    }

🔬 This loop draws 8 random lines connecting the fighters. What if you change 8 to 20? What if you change random(1, 4) to random(0.5, 2)?

    // A few random lines for web/tendril effects
    for (let i = 0; i < 8; i++) { // More lines
      stroke(random(0, 255), random(0, 255), random(0, 255), 150); // More opaque lines
      strokeWeight(random(1, 4)); // Thicker lines
      line(
        milesX + random(-20, 20),
        random(-40, 20),
        milesZ + random(-20, 20),
        venomX + random(-20, 20),
        random(-40, 20),
        venomZ + random(-20, 20)
      );
    }
function handleFightState() {
  if (fightState === 0) {
    // Miles Morales approaches Venom
    if (milesX < venomX - 50) {
      milesX += 1;
    } else {
      fightState = 1; // Miles is close, now Venom approaches
    }
  } else if (fightState === 1) {
    // Venom approaches Miles
    if (venomX > milesX + 50) {
      venomX -= 1;
    } else {
      fightState = 2; // Both are close, start the "fight"
    }
  } else if (fightState === 2) {
    // Simulated fight
    push();
    translate((milesX + venomX) / 2, -20, 0); // Center of the fight
    fill(255, 0, 0, 180); // Red "impact" effect, slightly more opaque
    noStroke();
    // Simple "explosion" or "impact" effect
    sphere(random(30, 50)); // Larger impact sphere
    pop();

    // A few random lines for web/tendril effects
    for (let i = 0; i < 8; i++) { // More lines
      stroke(random(0, 255), random(0, 255), random(0, 255), 150); // More opaque lines
      strokeWeight(random(1, 4)); // Thicker lines
      line(
        milesX + random(-20, 20),
        random(-40, 20),
        milesZ + random(-20, 20),
        venomX + random(-20, 20),
        random(-40, 20),
        venomZ + random(-20, 20)
      );
    }

    // After a short while, reset for continuous "fight"
    if (frameCount % 100 === 0) { // Every ~1.6 seconds (assuming 60fps)
      milesX = -200;
      milesZ = random(-100, 100);
      venomX = 200;
      venomZ = random(-100, 100);
      fightState = 0;
    }
  }
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

conditional Miles Approaches (State 0) if (fightState === 0) { if (milesX < venomX - 50) { milesX += 1; } else { fightState = 1; } }

Miles walks toward Venom from the left until they're within 50 units

conditional Venom Approaches (State 1) else if (fightState === 1) { if (venomX > milesX + 50) { venomX -= 1; } else { fightState = 2; } }

Venom walks toward Miles from the right until they're within 50 units of each other

conditional Combat (State 2) else if (fightState === 2) { ... sphere(...); ... line(...); ... if (frameCount % 100 === 0) { fightState = 0; } }

Displays collision effects between the fighters and resets every 100 frames

shape Impact Sphere sphere(random(30, 50));

Draws a red semi-transparent sphere between the fighters to show a collision

for-loop Collision Lines for (let i = 0; i < 8; i++) { ... line(...); }

Draws 8 random colored lines connecting the fighters to show energy discharge

conditional Fight Cycle Reset if (frameCount % 100 === 0) { ... fightState = 0; }

Every 100 frames (~1.6 seconds), resets characters and fight state to restart the cycle

if (fightState === 0) {
Checks if the current fight state is 0 (approach phase)
if (milesX < venomX - 50) {
Checks if Miles is more than 50 units away from Venom - if so, continue approaching
milesX += 1;
Moves Miles 1 unit per frame toward Venom (rightward, positive X)
fightState = 1;
When Miles gets close enough (within 50 units), transitions to state 1 so Venom starts approaching
if (venomX > milesX + 50) {
Checks if Venom is more than 50 units away from Miles
venomX -= 1;
Moves Venom 1 unit per frame toward Miles (leftward, negative X)
fightState = 2;
When both characters are within 50 units, transitions to state 2 (active combat)
translate((milesX + venomX) / 2, -20, 0);
Positions the impact effect at the midpoint between the two fighters
fill(255, 0, 0, 180);
Sets impact color to red with 180 alpha (semi-transparent)
sphere(random(30, 50));
Draws a sphere of random size (30-50 units) to represent an explosion or impact
for (let i = 0; i < 8; i++) {
Loops 8 times to draw 8 random collision effect lines
stroke(random(0, 255), random(0, 255), random(0, 255), 150);
Sets each line to a random RGB color with 150 alpha for variety and energy effects
line(milesX + random(-20, 20), random(-40, 20), milesZ + random(-20, 20), venomX + random(-20, 20), random(-40, 20), venomZ + random(-20, 20));
Draws a line from a random point near Miles to a random point near Venom, creating chaotic collision effect lines
if (frameCount % 100 === 0) {
Checks if the frame count is divisible by 100 (every 100 frames at 60fps = ~1.6 seconds)
milesX = -200;
Resets Miles back to starting position on the left
fightState = 0;
Resets the fight state back to 0, so the approach sequence starts again

windowResized()

windowResized() is a p5.js lifecycle function that fires whenever the user resizes their browser window. Without it, the canvas would stay at its original size and not expand to fill the new window dimensions. This is essential for responsive, fullscreen sketches.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Re-adjust camera if needed, but orbitControl() handles it well
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

function-call Canvas Resize resizeCanvas(windowWidth, windowHeight);

Resizes the p5.js canvas to match the current window dimensions

function windowResized() {
windowResized() is a p5.js built-in function that runs automatically whenever the browser window is resized
resizeCanvas(windowWidth, windowHeight);
Resizes the canvas to match the new window size so the sketch remains full-screen

📦 Key Variables

milesX number

Stores Miles Morales' current X position on the canvas - changes as he approaches Venom during each fight cycle

let milesX = -200;
milesZ number

Stores Miles Morales' depth position (Z axis) - randomized at start and reset for variety each cycle

let milesZ = random(-100, 100);
venomX number

Stores Venom's current X position - changes as he approaches Miles

let venomX = 200;
venomZ number

Stores Venom's depth position (Z axis) - randomized like Miles for visual variety

let venomZ = random(-100, 100);
fightState number

Tracks which phase of the fight sequence is active: 0 = Miles approaches, 1 = Venom approaches, 2 = active combat

let fightState = 0;
buildings array

Stores objects representing each building in the city - each object has x, z, h (height), w (width), d (depth) properties

let buildings = [];

🔧 Potential Improvements (6)

Here are some ways this code could be enhanced:

PERFORMANCE drawBuildings() window rendering

Windows are drawn every frame with random() checks - this recalculates which windows are lit 60 times per second, causing visual flickering

💡 Pre-generate window states in generateBuildings() by storing a boolean array for each building that determines which windows are lit, then use that stored state instead of random(1) > 0.3 each frame

BUG handleFightState() approach logic

Characters move at a fixed 1 pixel/frame regardless of frame rate - if frame rate drops, approach becomes slower, breaking the timing

💡 Use deltaTime or calculate velocity based on target time: milesX += (venomX - 50 - milesX) * 0.05 for frame-rate-independent movement

FEATURE isBuildingOverlapping()

The function is stubbed out and always returns false, so buildings can overlap heavily

💡 Implement actual collision detection: loop through previously placed buildings, calculate distance between new position and each building, return true if distance < minDist + other building radius

STYLE Global variable declarations

Variables are declared at global scope without clear organization - harder to track what state exists

💡 Consider using an object to group related variables: let gameState = { milesX: -200, milesZ: 0, venomX: 200, ... } for cleaner code structure

BUG drawBuildings() nested loops with translate()

The window grid loops use cumulative translate() calls that drift over multiple rows - later rows may have misaligned windows

💡 Use push/pop around the inner loop or recalculate position each iteration: translate(startX_w + x_w, startY + y, d/2 + 1) instead of cumulative translate

PERFORMANCE draw() - lighting setup

Three directionalLight() calls happen every frame even though they never change

💡 Move lighting setup into setup() instead of draw() - lights only need to be set once unless the sketch allows dynamic lighting changes

🔄 Code Flow

Code flow showing setup, draw, drawmilesmorales, drawvenom, drawgroundandroads, generatebuildings, drawbuildings, isbuildingoverlapping, handlefightstate, windowresized

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

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Creation] setup --> character-positioning[Character Positioning] setup --> camera-setup[Camera Setup] setup --> building-generation[Building Generation] setup --> lighting-setup[Lighting Setup] setup --> camera-control[Camera Control] setup --> draw[draw loop] click setup href "#fn-setup" click canvas-creation href "#sub-canvas-creation" click character-positioning href "#sub-character-positioning" click camera-setup href "#sub-camera-setup" click building-generation href "#sub-building-generation" click lighting-setup href "#sub-lighting-setup" click camera-control href "#sub-camera-control" draw --> miles-rendering[Miles Rendering] draw --> venom-rendering[Venom Rendering] draw --> drawgroundandroads[Draw Ground and Roads] draw --> drawbuildings[Draw Buildings] draw --> handlefightstate[Handle Fight State] click draw href "#fn-draw" click miles-rendering href "#sub-miles-rendering" click venom-rendering href "#sub-venom-rendering" click drawgroundandroads href "#fn-drawgroundandroads" click drawbuildings href "#fn-drawbuildings" click handlefightstate href "#fn-handlefightstate" drawgroundandroads --> transform[Transform] drawgroundandroads --> ground-plane[Ground Plane] drawgroundandroads --> horizontal-roads[Horizontal Roads] drawgroundandroads --> vertical-roads[Vertical Roads] click transform href "#sub-transform" click ground-plane href "#sub-ground-plane" click horizontal-roads href "#sub-horizontal-roads" click vertical-roads href "#sub-vertical-roads" drawbuildings --> variable-init[Variable Init] drawbuildings --> main-loop[Main Loop] drawbuildings --> storage[Storage] click variable-init href "#sub-variable-init" click main-loop href "#sub-main-loop" click storage href "#sub-storage" main-loop --> placement-loop[Placement Loop] placement-loop --> isbuildingoverlapping[Is Building Overlapping] placement-loop --> random-properties[Random Properties] click placement-loop href "#sub-placement-loop" click isbuildingoverlapping href "#fn-isbuildingoverlapping" click random-properties href "#sub-random-properties" drawbuildings --> iteration[Iteration] iteration --> main-box[Main Box] iteration --> window-setup[Window Setup] click iteration href "#sub-iteration" click main-box href "#sub-main-box" click window-setup href "#sub-window-setup" miles-rendering --> body[Body] miles-rendering --> accents[Accents] miles-rendering --> chest-symbol[Chest Symbol] miles-rendering --> head[Head] miles-rendering --> limbs[Limb] miles-rendering --> web-pattern[Web Pattern] click body href "#sub-body" click accents href "#sub-accents" click chest-symbol href "#sub-chest-symbol" click head href "#sub-head" click limbs href "#sub-limbs" click web-pattern href "#sub-web-pattern" venom-rendering --> venom-body[Venom Body] venom-rendering --> spider-symbol[Spider Symbol] venom-rendering --> venom-head[Venom Head] venom-rendering --> venom-limbs[Venom Limbs] venom-rendering --> tendrils[Tendrils] click venom-body href "#sub-venom-body" click spider-symbol href "#sub-spider-symbol" click venom-head href "#sub-venom-head" click venom-limbs href "#sub-venom-limbs" click tendrils href "#sub-tendrils" handlefightstate --> state-0[State 0] handlefightstate --> state-1[State 1] handlefightstate --> state-2[State 2] handlefightstate --> fight-animation[Fight Animation] click state-0 href "#sub-state-0" click state-1 href "#sub-state-1" click state-2 href "#sub-state-2" click fight-animation href "#sub-fight-animation" state-2 --> impact-effect[Impact Effect] state-2 --> effect-lines[Effect Lines] state-2 --> reset[Reset] click impact-effect href "#sub-impact-effect" click effect-lines href "#sub-effect-lines" click reset href "#sub-reset" windowresized --> resize-call[Resize Call] click windowresized href "#fn-windowresized" click resize-call href "#sub-resize-call"

❓ Frequently Asked Questions

What visual experience does the spider man 2 test sketch provide?

The sketch creates a 3D city environment featuring stylized characters of Miles Morales and Venom, set against a dynamic sky backdrop with buildings and roads.

How can users interact with the spider man 2 test sketch?

Users can interact by moving the camera around using mouse controls, allowing them to explore the 3D scene from different angles.

What creative coding concepts are showcased in the spider man 2 test sketch?

The sketch demonstrates concepts of 3D rendering, lighting effects, and basic animation techniques within the p5.js framework.

Preview

spider man 2 test - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of spider man 2 test - Code flow showing setup, draw, drawmilesmorales, drawvenom, drawgroundandroads, generatebuildings, drawbuildings, isbuildingoverlapping, handlefightstate, windowresized
Code Flow Diagram