Nine agent skills for building Makepad/Rust games, ported from majidmanzarpour/threejs-game-skills. Same director-routed workflow and premium bar; runtime rewritten for this fork's game crates. - makepad-game-director entrypoint, routing, continuity, asset probe - makepad-gameplay-systems loop, movers vs rigid bodies, input, camera, netplay - makepad-aaa-graphics-builder lighting, shaders, budget, visual scorecard - makepad-game-ui-designer HUD, menus, touch and XR UI - makepad-debug-profiler defect bisection and profiling - makepad-qa-release verification ladder, evidence, packaging - makepad-3d-generator CC0 model search, casts, procedural geometry - makepad-image-generator texgen textures, palettes, sky, icons - makepad-audio-generator sample bank, mixer, material impacts, 3D audio Written against the real APIs in libs/game/*, libs/sim and apps/arcade: the game.* verb table, GameRenderer adaptive quality, the packed 6-float GameMeshVertex layout, script_mod! splash styling, makepad-test driving, and the BUDGETS.md numbers. No paid generation API is required - the CC0 library plus seeded makepad-game-gen replaces them. Includes install.sh (Codex/Claude), validate-skills.sh and a repo-aware probe_assets.sh; both scripts verified against this checkout.
6.8 KiB
| name | description |
|---|---|
| makepad-debug-profiler | Diagnose and fix Makepad game defects: black or empty viewport, missing models, broken shaders, wrong lighting, input that does nothing, stutter and frame spikes, memory growth, netplay desync, script errors, and mobile/XR-specific failures. Use for 'nothing renders', 'it compiles but crashes on launch', 'assets don't appear', 'it's slow', and profiling work. |
Makepad Debug Profiler
Find the actual cause. The most expensive debugging in this codebase comes from fixing a symptom one layer above the fault.
Method
- Reproduce deterministically. Fix the seed, fix the tick count, note the platform and build profile. An intermittent bug you cannot reproduce is a bug you cannot verify fixed.
- Bisect the pipeline, not the source file. The layers are: sim state → scene state → instance data → draw call → shader → pass composite. Find the last layer where the data is right.
- Print the data, not the code. Assert what the values actually are at that boundary.
- Fix the cause, then add the test that would have caught it.
Layer bisection for "nothing renders"
Work down; the first failing check is your bug.
// 1. does the sim have anything?
println!("entities: {}", world.entities.len());
// 2. is it where you think?
println!("player pos: {:?}", player.pos);
// 3. is the camera looking at it?
println!("cam: {:?} target {:?}", rig.pos, rig.target);
// 4. did instances reach the renderer?
println!("models: {} particles: {}", models.len(), particles.len());
// 5. did the model actually load?
println!("loaded: {}", renderer.model_is_loaded("kenney/racing/vehicle-truck-yellow"));
println!("bounds: {:?}", renderer.model_bounds(id));
// 6. is quality having silently dropped things?
println!("q{} because {}", renderer.quality_level(), renderer.quality_reason());
Most common causes, in the order they actually occur:
| Symptom | Usual cause |
|---|---|
| Black viewport | camera inside geometry, or near/far plane wrong, or the pass never composited |
| Everything invisible but HUD fine | camera target not updated from the sim; rig at origin |
| Model missing, no error | wrong asset id — it degrades to a primitive silently. Assert model_is_loaded |
| Model present but tiny/huge | scale mismatch between the kit and the player capsule |
| Model at origin | transform not applied; ModelInstance::new(.., transform) got identity |
| Flat/wrong lighting | light_dir set on some draw types and not others |
| Shadows vanished under load | adaptive quality dropped the budget — read quality_reason() |
Fine in cargo check, broken on launch |
the splash DSL resolves at runtime |
That last row is the most important fact in this skill: cargo check cannot validate the DSL. A malformed style, a missing widget id, or a bad inheritance path fails only when the app runs. Any UI or style change must be launched.
Silent asset fallback
The library is designed to degrade gracefully — with no assets downloaded the game runs on primitives and asset-dependent tests skip. Convenient for CI, dangerous when debugging: a typo'd id looks like a design choice.
bash skills/makepad-game-director/scripts/probe_assets.sh
Then assert, don't eyeball:
for id in scene_model_ids { assert!(renderer.model_is_loaded(id), "missing {id}"); }
Input that does nothing
Bisect the same way: does the platform event arrive → does the mapping layer produce an action → does the sim consume it → does the entity respond?
Specific traps: a radial deadzone applied per-axis; a ground raycast that hits the player's own capsule (so on_floor is always false, so jump never fires); an input buffer that is never cleared (double jumps); touch handled only as mouse (so nothing works on device); XR controller pose read as gamepad axes.
Performance
Measure before changing anything, in --release.
renderer.frame_p90_ms(); // report p90, not mean
renderer.quality_level();
renderer.quality_reason();
stats.instance_floats;
cargo run -p makepad-arcade --example bigworld_probe --release
Given the sim is ~0.2% of frame time, a slow frame is almost never gameplay code. Check, in order: skinned character count (CPU skinning re-uploads every vertex every frame), draw-call count, shadow casters, particle pool size, instance stream width, and only then fragment work.
Stutter is not the same as low framerate. A 16 ms mean with a 40 ms p90 is a stutter problem: look for per-frame allocation, model loading during play, terrain regeneration, or a HashMap rehash in the step loop. Preload and pool.
Two systems fighting is a classic here: if quality oscillates, check you have not added a frame pacer beside GameRenderer's adaptive controller.
Netplay desync
Host-authoritative, so a divergence means a client simulated something it should not have, or the sim is not deterministic. Check: variable dt anywhere; std float transcendentals instead of makepad-game-math; unseeded RNG instead of the seeded rng; HashMap iteration order inside step; clients sending state instead of input; content replicated as vertex data instead of (preset, seed, position).
Bisect by logging a per-tick state hash on host and client and finding the first differing tick.
Script route
Errors surface in EvalReport. Constraints that present as bugs: the 500k-instruction cumulative pool per tick shared by on_tick + timers + touch (symptom: handlers silently stop running late in a tick), and typo-guarded option keys (symptom: a verb rejects a key — the key is wrong, do not work around it). Untrusted scripts must pass strip_capabilities with a Trust level; a capability failure is the sandbox working.
Memory
Growth over time comes from unbounded Vecs: particle instances, decals (BulletDecal — cap and recycle), log buffers, model cache (makepad-game-gen::cache), or per-frame allocations never reused. Pool anything created per frame.
Tooling
cargo check -p <crate> for the fast loop; cargo test -p <crate> for Cx-free logic; cargo clippy (the repo has clippy.toml); makepad-test for driving the real app with widget_dump(), widget_snapshot(), screenshot(), wait_for_log_contains(); RUST_BACKTRACE=1 for panics; the ao_render probe to judge baked AO by eye.
When stuck
Re-read the actual API rather than guessing at a signature — makepad_game_script::api_text() prints the authoritative verb list, and the crate sources are in the tree. State what you have ruled out. Say plainly what you could not reproduce; an unverified fix reported as fixed is worse than an open bug.
Required reading
references/debug-playbook.md— symptom→cause tables and bisection recipes.references/profiling.md— measuring, budgets, what to cut.