Level Design from Measured Metrics, Checked by a Bot
7 min read

The most frustrating thing in a platformer is a platform you cannot reach. Players try twenty times, then delete the game. AI agents can generate a course very quickly, but who proves that a quickly generated course can actually be cleared?
In BOINGWARD, the answer has two parts: courses are generated from the pogo's measured movement values, not from hand-picked numbers, and a bot plays every course with the real pogo physics. This post covers both, and how the medal times come out of that same bot. How the pogo bounces is in the physics post, and the overall story is in the series hub post.
Measure first, then build
The rule in the design doc is "metrics first": once the physics tuning settles, measure the standard bounce height, the longest jump and the air time, and build the platform kit around those numbers. In BOINGWARD the measurements live in one file, CourseMetrics. All of them were measured at 60 physics steps per second by a test that runs without a screen:
| Metric | Value | Meaning |
|---|---|---|
| Lowest bounce apex | 3.0 m | Tip height above the pad on the slowest launch |
| Steady bounce apex | 4.0 m | After the combo settles, with no input |
| Time between bounces | 1.18 s | Steady state on flat ground |
| First hop from rest | 8.7 m | Stick fully forward, right after a respawn |
| Longest running hop | 10.2 m | Held in check by the horizontal speed cap |
| Headroom needed | 7.5 m | Steady apex + body + margin |
The tool that builds courses never uses these numbers directly, only ratios of them. When the physics tuning changes, the measurements are refreshed, the course is rebuilt with tools/build_course.gd, and every gap adapts to the new values automatically.
The worst case: a freshly respawned player
Every required gap is measured against the "first hop from rest", not the "longest running hop". The reason is simple: when players fall and respawn at a checkpoint, their speed is zero. If the course contains a gap only a player with momentum can clear, every fall after that point leaves the player stuck.
That is why the Spire course tests set two limits:
- A required gap can be at most 60 percent of the first hop from rest: 8.7 x 0.6 ≈ 5.2 meters.
- A step up can be at most 75 percent of the lowest bounce apex: 3.0 x 0.75 = 2.25 meters.
The long climb mode is a bit stricter: 55 percent for gaps and 70 percent for height. The hop after a launcher such as a trampoline or a cannon may not exceed 75 percent of that launcher's own apex.
Readability is tested too
The design doc spells out the difficulty order: wide static platforms first, then gaps, narrow pads, slopes, moving platforms, chained precision landings, recovery routes and, last, expert shortcuts. Each step teaches one new "verb".
Besides keeping that order, the course tests measure other things: whether there is 7.5 meters of headroom above platforms (so the pogo's head does not hit the next platform), whether the player can see the next one to three targets, and whether the whole course fits the phone budget. For the Spire the limit is 60 draw calls and 40,000 triangles, excluding the player, UI and sky.
Checkpoints changed because of a rule too. In the first version, a checkpoint fired the moment the player entered its invisible volume, even while flying past. Now a checkpoint only counts when the pogo's tip lands on its pad. The Spire has five checkpoints and a finish, and the finish follows the same rule.
The bot: trying every jump with the real physics
Static rules say "possible on paper". To show it is really possible, a bot plays the course with the real PogoController. Its method was written to resemble how a player thinks:
- Hop by hop: Each attempt starts with a respawn on the previous platform, so the success of one hop never depends on luck in the hop before.
- Commit once: The bot only decides on a target as the next bounce approaches, the way a player chooses when to jump. Once committed, it does not change its mind; it only corrects direction in the air.
- Approach hop: If the target is beyond comfortable reach, the bot first makes a small hop toward the edge of its current platform.
- Moving targets: The bot predicts where a moving platform will be at the moment of landing. For timing-based targets it tries three different phases; landing on one is enough. It has enough time to wait out two full cycles of the slowest mechanism.
- Timed platforms: Pieces such as breaking glass or crumbling slabs are reset to their starting state before the bot begins, and the bot only sets off when its first landing will hold.
The long climb mode runs a bigger version of the same approach: the bot lands every one of 804 hops from the bottom to the summit with the real pogo, and the test takes about 20 seconds. The bot does no tricks; no platform should require one.
A bot is a better pilot than a person
One comment in the bot's code sums up the whole approach: "It is a better pilot than a person, so the layout's static envelope is the fairness check; this proves physical reachability."
That distinction matters. The bot steers with millisecond precision and never gets tired. Not every hop the bot can make is fair to a player. So there are two separate checks, and both have to pass: the static limits answer "is this reasonable for a human?", and the bot answers "is this really possible with the real physics?". If the bot cannot clear it, the course is definitely wrong; if the bot can, that is only a necessary condition.
Medal times come from the bot
Guessing medal times is easy but inconsistent: when the course changes, the times become either impossible or meaningless. In BOINGWARD the base time is calculated from the measure of a clean run:
base time = hops x 1.18 s + timed pieces x 2.5 sThe Spire's level data counts 61 hops and 9 pieces that require waiting: 61 x 1.18 + 9 x 2.5 ≈ 94.5 seconds. The medals are multiples of that:
| Medal | Multiplier | Time |
|---|---|---|
| Gold | 1.3 | 2:02.8 |
| Silver | 1.8 | 2:50.1 |
| Bronze | 2.8 | 4:24.5 |
The times are not in the code but in data/levels/spire.tres, and the script that generates the level data recalculates the base time every time it runs. If the course changes, the medals adapt to the new route on their own; the only thing to tune by hand is the multipliers. The long climb mode has no medals; it tracks best height and summit time instead.
The phone budget is a level rule too
In the long climb mode, performance is part of the layout. The tower is split into 24-meter slices, and each slice may hold at most three moving and two static kit pieces. When the budget is full, the next kit piece is swapped for a simple prop from that zone, but launchers are never swapped, because the next hop is calculated from their apex. The worst view measured 40 draw calls and about 38,000 triangles.
Frequently Asked Questions
How do you decide gap distances in a platformer?
Measure the character's worst-case jump: usually the first hop with no speed right after a respawn. Keep required gaps at a fraction of that (55 to 60 percent in BOINGWARD). Save longer gaps for optional shortcuts.
Is it worth writing a bot for level testing?
If your courses are generated by code or by AI agents, absolutely. A bot checks within seconds after every change that a platform is reachable with the real physics. But because a bot plays better than a person, it cannot be the measure of fairness; that needs separate static limits.
How should medal times be calculated?
Instead of guessing, derive them from a clean run: hop count times air time, plus waiting time for timed pieces. Make the medals multiples of that time. When the course changes, the times update themselves.
Related Posts
Risk and Reward: Designing Air Tricks for a Pogo Game
From a one-sentence wish to a system defined by numbers: how air tricks in BOINGWARD became an optional, risky reward that does not depend on frame rate.
Cheat Checks on Mobile: Server-Authoritative Racing in Godot
BOINGWARD's 8-player race server: why WebSocket, how movement is validated, and why honest bots were flagged as cheaters in a test over a phone's mobile data.
Pogo Stick Physics in Godot: A Character That Bounces Itself
BOINGWARD's pogo physics: auto-bounce with RigidBody3D, the launch formula, taming a snowballing speed, and a test that matches at 30, 60 and 144 fps.