setup()
setup() runs once when the sketch starts and is responsible for all initialization. Notice how it both creates physics objects AND registers event listeners—this pattern is essential for games. The collision detection here is surprisingly subtle: collisionStart tells us when contact begins, but collisionEnd needs to re-check all pairs because Spider-Man might transition from one building to another without ever leaving the air. This is what makes the grounded detection robust.
🔬 This loop creates 10 buildings at startup. What happens if you change initialBuildingCount to 20 in the constants at the top and then change this loop to use 20 instead of initialBuildingCount?
// 4. Generate initial buildings for the city
for (let i = 0; i < initialBuildingCount; i++) {
addBuilding(); // Call our helper function to add a building
}
🔬 This condition checks 'playerBody.position.y < otherBody.position.y' to verify Spider-Man is above the surface. What happens if you remove that check? Can you jump while touching the side of a building?
if (bodyA === playerBody || bodyB === playerBody) {
// The other body in the collision
const otherBody = (bodyA === playerBody) ? bodyB : bodyA;
// Check if the other body is static (ground or a building)
// AND if Spider-Man is positioned above the other body (meaning he's landing on it)
if (otherBody.isStatic && playerBody.position.y < otherBody.position.y) {
isGrounded = true; // Set grounded flag to true
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
// Tip: Use windowWidth/windowHeight for responsive canvas
// 1. Setup Matter.js engine and world
engine = Engine.create();
world = engine.world;
world.gravity.y = gravity; // Set Matter.js gravity
// 2. Create Spider-Man's physics body
playerBody = Bodies.rectangle(
100, // Initial X position
height - playerSize - 100, // Initial Y position (above ground)
playerSize, // Width
playerSize * 1.5, // Height (make him taller than wide)
{
frictionAir: 0.05, // Air resistance
friction: 0.1, // Surface friction
restitution: 0.1, // How much he bounces
density: 0.001, // Make him light enough to jump well
label: 'player' // Give it a label for collision detection
}
);
World.add(world, playerBody); // Add Spider-Man to the physics world
// 3. Create the ground physics body
// It's static, so it doesn't move or fall.
ground = Bodies.rectangle(width / 2, height, width * 2, 20, {
isStatic: true, // Make it unmovable
friction: 0.8, // High friction to prevent sliding
label: 'ground' // Label for collision detection
});
World.add(world, ground);
// 4. Generate initial buildings for the city
for (let i = 0; i < initialBuildingCount; i++) {
addBuilding(); // Call our helper function to add a building
}
// 5. Setup collision event listeners to detect if Spider-Man is grounded
// 'collisionStart' fires when two bodies begin colliding
Events.on(engine, 'collisionStart', function(event) {
const pairs = event.pairs; // Get all collision pairs
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
let bodyA = pair.bodyA;
let bodyB = pair.bodyB;
// Check if one of the bodies in the pair is our player
if (bodyA === playerBody || bodyB === playerBody) {
// The other body in the collision
const otherBody = (bodyA === playerBody) ? bodyB : bodyA;
// Check if the other body is static (ground or a building)
// AND if Spider-Man is positioned above the other body (meaning he's landing on it)
if (otherBody.isStatic && playerBody.position.y < otherBody.position.y) {
isGrounded = true; // Set grounded flag to true
}
}
}
});
// 'collisionEnd' fires when two bodies stop colliding
Events.on(engine, 'collisionEnd', function(event) {
const pairs = event.pairs;
let currentlyGrounded = false; // Assume not grounded initially
// Re-check all current collisions involving the player
// This is necessary because 'collisionEnd' only tells us what STOPPED colliding.
// The player might still be colliding with another static body.
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
let bodyA = pair.bodyA;
let bodyB = pair.bodyB;
if (bodyA === playerBody || bodyB === playerBody) {
const otherBody = (bodyA === playerBody) ? bodyB : bodyA;
if (otherBody.isStatic && playerBody.position.y < otherBody.position.y) {
currentlyGrounded = true; // Found a collision, player is still grounded
break; // No need to check further pairs
}
}
}
// If after checking all current collisions, no static body is found below the player,
// then the player is truly no longer grounded.
if (!currentlyGrounded) {
isGrounded = false;
}
});
}
Line-by-line explanation (11 lines)
🔧 Subcomponents:
engine = Engine.create();
Creates a new Matter.js physics engine that will simulate gravity, velocity, and collisions
playerBody = Bodies.rectangle(100, height - playerSize - 100, playerSize, playerSize * 1.5, {...});
Creates a rectangular physics body for Spider-Man with properties that affect how he moves and interacts with the world
ground = Bodies.rectangle(width / 2, height, width * 2, 20, {isStatic: true, ...});
Creates a static (unmovable) rectangular physics body at the bottom of the canvas that Spider-Man can land on
for (let i = 0; i < initialBuildingCount; i++) { addBuilding(); }
Generates 10 random buildings ahead of the starting position to create the initial level
Events.on(engine, 'collisionStart', function(event) {...});
Registers a listener that fires when Spider-Man touches a building or ground, setting isGrounded to true if he lands from above
Events.on(engine, 'collisionEnd', function(event) {...});
Registers a listener that fires when Spider-Man leaves a surface, carefully checking if he's still touching something else before setting isGrounded to false
createCanvas(windowWidth, windowHeight);- Creates a canvas that fills the entire browser window, making the game responsive to window size
engine = Engine.create();- Creates a Matter.js physics engine that will handle all physics calculations like gravity and collisions
world = engine.world;- Gets a reference to the physics world contained in the engine, where all physics bodies live
world.gravity.y = gravity;- Sets the downward gravitational acceleration in the physics world using our gravity constant
playerBody = Bodies.rectangle(100, height - playerSize - 100, playerSize, playerSize * 1.5, {...});- Creates a rectangular physics body 100 pixels from the left and 100 pixels above the ground, sized to Spider-Man's proportions
World.add(world, playerBody);- Registers the Spider-Man physics body with the world so Matter.js will simulate its movement and collisions
ground = Bodies.rectangle(width / 2, height, width * 2, 20, {isStatic: true, ...});- Creates a wide, static ground rectangle that won't fall or move no matter what hits it
for (let i = 0; i < initialBuildingCount; i++) { addBuilding(); }- Loops 10 times, calling addBuilding() each time to generate the starting set of buildings
Events.on(engine, 'collisionStart', function(event) {...});- Registers a function that runs every time two physics bodies start touching, checking if Spider-Man landed on something static from above
if (otherBody.isStatic && playerBody.position.y < otherBody.position.y) { isGrounded = true; }- Sets isGrounded to true only if the object Spider-Man hit is static (not moving) AND Spider-Man's center is above the object's center (meaning he landed on top)
Events.on(engine, 'collisionEnd', function(event) {...});- Registers a function that runs when two physics bodies stop touching, but only sets isGrounded to false if Spider-Man has no other static surfaces beneath him