Module 02

2D Platformer

Weeks 3-4 | Physics, spatial design, and "game feel"

"A platformer lives or dies on whether the jump feels right."


Week 1: History & Design Theory

The Origin

Shigeru Miyamoto's Donkey Kong (Nintendo, 1981) created the platformer by asking a question no arcade game had asked before: what if the player moved through a space instead of defending one? Jumpman (later Mario) ran across girders, climbed ladders, and leaped over barrels. The jump — voluntary, committal, governed by a gravity arc — became the foundational verb of an entire genre.

How the Genre Evolved

Super Mario Bros. (Nintendo, 1985) defined side-scrolling level design. Miyamoto and Takashi Tezuka built World 1-1 as a wordless tutorial: the first Goomba teaches you about enemies, the first block teaches you to hit things, the first mushroom teaches you about power-ups. It introduced the concept of a level as a designed experience with pacing and rhythm.

Celeste (Matt Thorson / Extremely OK Games, 2018) represents the modern evolution: extremely precise movement with generous player-assistance systems. These invisible affordances make a hard game feel fair rather than cheap:

What Makes Platformers "Great"

"Game feel" — the term coined by Steve Swink. The player's avatar is an instrument, and the levels are the sheet music. Great platformers have carefully tuned acceleration curves, responsive controls, and animation that communicates momentum. None of this is visible to the player, but all of it is felt.

The Essential Mechanic

The jump. A single committal action with a gravity arc that the player must time and aim. Everything else in the genre is built around making that one verb endlessly interesting.


Week 2: Build the MVP

What You're Building

A single-screen (or short-scrolling) level with platforms the player can jump between. That's the core. If the jump feels good, you've succeeded.

Core Concepts (Must Implement)

1. Gravity & Acceleration-Based Movement

Apply a constant downward acceleration to the player's y-velocity each frame:

vy += gravity * dt
y += vy * dt

This produces a parabolic jump arc — fundamentally different from Pong's linear ball motion.

Why it matters: This is Euler integration. The pattern (accumulate forces → update velocity → update position) is the foundation of every physics system from Phaser's Arcade to Box2D to Havok.

Interactive: Gravity & Jump Arc

Use the sliders to adjust gravity and jump force, then press Spacebar or click "Jump" to launch the character. A dotted path traces the parabolic arc. Watch how the velocity values update in real-time.

vy: 0

2. Grounded-State Detection

Track whether the player is standing on a surface. Only allow jump input when isGrounded === true.

Why it matters: Input processing must be context-dependent. This pattern recurs in dashing, wall-jumping, attacking — any ability with preconditions.

3. Tilemap / Level Data

Represent the level as a 2D array of integers:

0 = air
1 = solid ground
2 = hazard (stretch)

Render and collide against it by mapping world coordinates to grid indices.

Why it matters: Tilemaps are the most common spatial data structure in 2D games (roguelikes, RTSs, puzzle games). They make collision lookups fast by converting world positions directly to grid indices.

4. Tile-Based Collision Resolution

Resolve collisions between the player (a moving AABB) and static tile geometry:

  1. Check tiles adjacent to the player
  2. Compute penetration depth on each axis
  3. Push the player out along the shallowest axis first

Resolving one axis before the other prevents corner-catching and tunneling bugs.

Why it matters: Axis-separated resolution is the standard approach in 2D engines. Understanding the why prevents a class of bugs that plagues beginners.

Interactive: Tilemap Collision

Click tiles to toggle them solid (brown) or empty. Use Arrow keys or WASD to move the character. The character falls with gravity and collides with solid tiles. Tiles being checked for collision are highlighted in yellow.

Click tiles to build, arrow keys to move

5. Player State Machine

An explicit FSM: Idle, Running, Jumping, Falling. Each state governs:

Why it matters: The State pattern applied to games. Scales to enemy AI, menu flow, network state, and any behavior with distinct modes.

Interactive: Player State Machine Visualizer

Use Arrow keys or WASD to control the character below. The FSM diagram above highlights the current state in real-time. Watch how states transition as you move, jump, and land.

6. Camera / Viewport Scrolling

If your level is larger than one screen, translate all rendered positions by a camera offset so the player stays centered (or within a deadzone).

Why it matters: Introduces the distinction between world space and screen space — fundamental to every game with a movable viewport.

7. Sprite Animation

Cycle through sprite frames based on the player's state. Idle loops, run cycles, jump/fall frames.

Why it matters: Visual output reflects logical state. The renderer displays what the state machine dictates — it never drives it.


Why "Game Feel" Matters: Coyote Time

One of the most important invisible affordances in a platformer is coyote time — a brief grace period after walking off a ledge during which the player can still jump. Without it, players who press jump one frame late simply fall. With it, the game feels generous and responsive.

Interactive: Coyote Time Comparison

Click "Run" on each side to see the difference. Left: NO coyote time (the character cannot jump once it leaves the ledge). Right: WITH coyote time (6-frame grace period). The timeline bar shows the grace window. The difference is viscerally obvious.


Putting It All Together

Below is a mini playable platformer combining every concept above: gravity, tile collision, a state machine, coyote time, and collectibles. Three small levels with platforms, coins, and a goal.

Interactive: Mini Playable Platformer

Arrow keys or WASD to move, Space to jump. Collect all the coins and reach the flag to advance. Three levels of increasing difficulty.

Level 1 Coins: 0/0

Stretch Goals (If Time Allows)

MVP Spec

FeatureRequired
Player character with gravityYes
Jump with parabolic arcYes
Tile-based level (at least one screen)Yes
Solid platform collision (no falling through)Yes
Player state machine (idle, run, jump, fall)Yes
Camera follow (if level scrolls)Yes
Coyote time / input bufferingStretch
CollectiblesStretch
Hazards / deathStretch

Deliverable

Analogies by Background

These analogies map game dev concepts to patterns you already know. Find your background below.

For Backend Developers

ConceptAnalogy
Gravity & AccelerationLike an accumulator pattern — forces accumulate into velocity, velocity into position, evaluated each tick of a scheduler
Grounded-State DetectionLike a precondition guard on an API endpoint — certain actions are only valid when the system is in a specific state
Tilemap / Level DataLike a spatial index or hash map with O(1) lookup — convert world coordinates to grid indices for instant collision queries
Tile-Based Collision ResolutionLike conflict resolution in concurrent writes — detect overlap, determine the smallest correction, apply it in priority order
Player State MachineLike a connection or session state machine (IDLE → ACTIVE → CLOSING) — each state governs valid transitions and behavior
Camera / Viewport ScrollingLike pagination or a sliding window over a large dataset — you only render the visible subset, offset by the current position
Sprite AnimationLike a view layer driven by state — the renderer reads the current state and selects the appropriate representation, never the reverse

For Frontend Developers

ConceptAnalogy
Gravity & AccelerationLike a CSS transition with ease-in — acceleration gives the jump a natural curve rather than linear movement
Grounded-State DetectionLike disabling a button until a form is valid — the jump action is gated by a boolean precondition
Tilemap / Level DataLike a CSS Grid layout where each cell is a component — the 2D array defines what occupies each grid position
Tile-Based Collision ResolutionLike resolving overlapping absolutely-positioned elements — detect the overlap, compute how much to push back, resolve one axis at a time
Player State MachineLike a React useReducer or component lifecycle — state determines which inputs are accepted and what renders
Camera / Viewport ScrollingLike overflow: scroll on a container — the viewport clips the visible area and translates based on scroll position
Sprite AnimationLike swapping CSS classes based on state — idle, running, jumping each trigger a different animation keyframe set

For Data / ML Engineers

ConceptAnalogy
Gravity & AccelerationLike Euler integration of an ODE — v += a*dt, x += v*dt is the simplest numerical integration scheme
Grounded-State DetectionLike a conditional mask — is_grounded is a boolean gate that filters which operations can execute, like np.where()
Tilemap / Level DataLike a 2D numpy array or sparse matrix — each cell holds a tile type, and coordinate-to-index conversion is simple integer division
Tile-Based Collision ResolutionLike resolving constraint violations in optimization — find the minimum penetration vector and project along it to satisfy the constraint
Player State MachineLike a Markov chain with deterministic transitions — current state plus input determines the next state
Camera / Viewport ScrollingLike slicing a window from a large tensor — world[cam_y:cam_y+h, cam_x:cam_x+w] extracts the visible region
Sprite AnimationLike selecting a visualization based on a categorical variable — the state label indexes into a lookup table of frame sequences

Discussion Questions

  1. What's the difference between "realistic" gravity and gravity that "feels right"? Try doubling your gravity constant while also doubling jump force — how does it change the feel?
  2. Why resolve collision on one axis before the other? What happens if you resolve both simultaneously?
  3. How does Super Mario Bros. World 1-1 teach the player without any text? What design principles are at work?

Recommended Reference

If you want to go deeper on game feel and platformer mechanics, Matt Thorson (Celeste developer) and Maddy Thorson shared detailed breakdowns of Celeste's movement system. The key insight: good game feel is built from dozens of tiny, invisible lies — coyote time, input buffering, corner correction, variable jump height — that make the game feel more responsive than the raw physics would allow.