A level is a sequence of intentional experiences delivered through space.
Good level design is a process: define the metrics movement is built on, block
out geometry with primitives, play it, then dress it — never the reverse. This
skill is the engine-neutral practice; use godot-tilemap/unity-tilemap-2d to
lay out 2D grids and gridmaps for 3D.
When not to use: to generate levels algorithmically, use procedural-gen
(authored and procedural design are complementary). For the engine's tile/grid
painting tools, use godot-tilemap / unity-tilemap-2d. For the movement
abilities the metrics come from, that's the engine movement skill + input-systems.
# Measure the character ONCE, then size geometry in these units. If the jump
# changes, gaps must be re-derived — never eyeball reachability.
const RUN_SPEED := 240.0 # px/s (or m/s in 3D)
const MAX_JUMP_H := 96.0 # peak height of a full jump
const MAX_JUMP_DIST := 200.0 # horizontal distance of a running jump
const SAFE_GAP := MAX_JUMP_DIST * 0.7 # comfortable, not pixel-perfect
const HARD_GAP := MAX_JUMP_DIST * 0.95 # a deliberate skill check
# Build platforms so required jumps use SAFE_GAP; reserve HARD_GAP for optional reward.
A reachable level falls out of honest metrics. A platform placed MAX_JUMP_DIST + 1 away is impossible; one at SAFE_GAP is fair. Keep these constants beside the
level data so designers and code agree.
# Author the level as a sequence of beats with an intended intensity (0..1).
# This makes the pacing curve explicit and reviewable before you build rooms.
const BEATS := [
{ "room": "entry", "type": "teach", "intensity": 0.1 },
{ "room": "hall_1", "type": "combat", "intensity": 0.5 },
{ "room": "vista", "type": "rest", "intensity": 0.1 }, # breather + reward
{ "room": "gauntlet", "type": "combat", "intensity": 0.8 },
{ "room": "save_room", "type": "rest", "intensity": 0.2 }, # before the boss
{ "room": "boss", "type": "climax", "intensity": 1.0 },
]
# Read the intensity column top-to-bottom: it should rise overall but dip for rests
# (a sawtooth), never flatline high. Drive spawns/music intensity from this.
# Model the level as rooms + gated connections. Validate that the goal is
# reachable with the keys/abilities the player can actually obtain in order.
const ROOMS := {
"entry": { "exits": [ { "to": "hall_1" } ] },
"hall_1": { "exits": [ { "to": "vista", "needs": "double_jump" },
{ "to": "side_room" } ] }, # optional branch
"side_room": { "exits": [ { "to": "hall_1" } ], "grants": "double_jump" },
"vista": { "exits": [ { "to": "boss", "needs": "red_key" } ] },
}
# Validation (do this!): from "entry", can the player reach "boss" given that
# "double_jump" is granted in "side_room" before "vista" requires it? A flood
# fill that only traverses an exit when its `needs` is already satisfiable
# proves the critical path isn't soft-locked.
procedural-gen for variety; hand-author for intent.references/pacing-and-flow.md — the difficulty/tension curve in depth,
teaching-loop design (introduce→develop→twist→test), readability and guidance
techniques, 2D vs 3D layout considerations, and a blockout review checklist.godot-tilemap, unity-tilemap-2d — paint 2D level grids; gridmaps for 3D.procedural-gen — generate variety to complement authored structure.game-ai — encounter enemies that navigate the space you build.platformer, puzzle, roguelike — genres that compose this skill.