makepad/examples/gamemaker/resources/template/CLAUDE.md
Admin 4cfb51877c PerfGraph: generic frame profiler widget + Cx perf monitor channels
platform: Cx.perf_monitor — per-frame ring (240) of paint-to-paint gap +
per-channel CPU us. Built-in channels: event dispatch (outermost, minus
app-attributed time), script exec, GC, pass encode, nextDrawable wait.
Apps register custom channels: cx.perf_monitor.channel("physics", rgb).
Off until enabled; hooks in event dispatch, macos repaint, metal draw_pass.

widgets: PerfGraph — corner-pinned live panel (DrawVector strips): frame-gap
bars colored against 120/60Hz budgets with guide lines, stacked per-channel
CPU, legend with averages. Self-positions bottom-right (DrawVector geometry
+ deferred turtle alignment don't mix — no aligning parent).

gamemaker: PerfGraph hovers the game pane (F3 toggles), engine feeds script
+ physics channels (incl. hot-reload evals); engine text overlay moved to
F4; per-phase engine window kept for ag perf / AIGAME_PERF=1; new 'ag perf'
harness verb + template guidance (template CLAUDE.md force-added: runtime
resource, blanket CLAUDE.md gitignore had kept it untracked).

Measured on my-game-5: engine frame CPU ~0.3ms; the hiccup is frame pacing —
the 8ms NSTimer paint clock beats against the 120Hz display, the drawable
pool drifts full and nextDrawable blocks the main thread in a ~25-frame
sawtooth (avg 2-3.4ms, spikes 20-30ms). The graph shows it as red ramps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:10:27 +02:00

2.9 KiB
Raw Permalink Blame History

PROJECT_NAME — a kid's game, live inside Makepad

A child asks for changes by voice through the Game Maker app; you make them by editing game.splash — the whole game is that one file, a Makepad splash script evaluated live. Every clean edit hot-reloads the world the kid is watching, instantly, while you keep working. A broken edit never replaces the running game — the last working world stays up and the error waits for you in ./tools/ag errors.

The complete game.* API reference is in your system prompt (the Splash Game DSL Guide). Verbs not listed there do not exist. This file is the project-local workflow.

Checking your work — ALWAYS

  1. After every edit run ./tools/ag errors. Empty = your edit is live in front of the kid. An error = the kid still sees the OLD world; fix it. (Broken edits and runtime crashes are also reported back to you automatically as a "⚠ game error" message — but ag errors after each edit remains YOUR check; don't wait to be told.)
  2. To playtest: ./tools/ag test 120 tools/tapes/selftest.json restarts the game, replays the frame-numbered input tape, and writes .agent/sheet.png (a grid of frames over time) + .agent/probe.txt (pos/vel of probed tags every 15 frames). Read the image, and read the numbers — "the jump clears the step" should be a probe line you saw, not a hope.
  3. ./tools/ag peek = 4 screenshots of the live game + entity state, without interrupting the kid.
  4. ./tools/ag logs tails the game log (your game.log() lines + eval reports).
  5. If the game feels slow: ./tools/ag perf — per-phase frame profile (script, physics, draw). Keep script under ~2ms; if wait dominates, it's not your game's fault, leave it be.

Tapes are JSON: {"probe": ["player"], "events": [{"f":5,"press":"right"}, {"f":30,"press":"jump"},{"f":33,"release":"jump"}]} — actions are the input names (left right up down jump shoot grab). Same tape, same frames: runs are repeatable.

House style

  • Everything is procedural: colored shapes and synthesized sound. No image, model, or audio files — no files besides game.splash.
  • Give creatures a face (game.part eyes) and a name (game.label), and give actions sounds (game.sfx) — without being asked. That's what makes it real.
  • Outdoor game? game.sky({}) + game.terrain({smooth: true, bands: [...]}).
  • Small, visible changes. Tune constants and add shapes; avoid big rewrites.
  • Movers are ~0.8×1.6×0.8. Keep the playfield within the terrain you built.

Gotchas

  • Hex colors containing the letter e need the #x prefix: #x2ecc71.
  • let and fn go at the TOP of the file, before other statements.
  • The file re-runs from the top on each edit — don't accumulate state across edits. game.time() restarts at 0 on every reload.
  • on_touch fires every overlapping tick — latch or remove, or you'll play 60 win jingles a second.