makepad/apps/arcade/README.md
Admin 0ac21bdc9d A real cast: 36 rigged characters across 3 shared rigs — and Kenney IS rigged
THE FINDING THAT CHANGES THE PREMISE: "1 rigged model in 4,442" was measured
with the BROKEN GLB probe (the gLUF magic bug). With the probe fixed the
library holds 36 rigged models across three rigs:
  41 joints — 9 KayKit heroes + undead, up to 95 clips
   7 joints — 22 KENNEY civilians (male/female a-f, orc, human, archer, shop
              employees, skaters, soldiers), 32 clips
   6 joints — 5 Kenney platformer characters, 25 clips
So a village can be populated with 22 visually distinct civilians TODAY, with
no third-party pack at all. Every conclusion drawn from that probe before it
was fixed needs re-checking, not just this one.

KayKit: 9 characters fetched (Adventurers + Skeletons), pinned by commit +
sha256, 37 MB, gitignored. CC0 verified by READING LICENSE.txt at each pinned
commit, recorded in the script header and CREDITS.toml.

THE SHARED RIG HOLDS ACROSS PACKS, proven rather than assumed: hashing the
joint-name list of all nine files yields the SAME digest — 41 joints, same
names, same order — despite two separate repositories. Skeleton clips are a
strict superset (95 = the adventurers' 76 + 19 undead extras: awaken,
resurrect, spawn, taunt). So a clip authored for the knight plays on the
skeleton warrior and one animation path drives the cast. A test pins this,
including that both packs are present, so a version bump cannot silently break
it.

The texture trap that cost the Kenney fetch three attempts does NOT apply:
KayKit GLBs EMBED their atlas (image/png in a bufferView), verified by parsing
all nine.

tests/rigged.rs parses all 36 rigged models through makepad_game_render::skin
— the loader the app actually runs — and asserts the index's joint and clip
counts match it. Deliberate: the index's own probe was wrong for the entire
library once and survived because the fixture encoded the same error.

find_cast groups by JOINT COUNT rather than pack, because the valuable fact is
cross-pack interchangeability. Cast states are the INTERSECTION, not the union
— advertising a state one member cannot perform is worse than a shorter list.
Added the state words the skeletons needed (spawn/resurrect/taunt/use):
Skeletons_Awaken_Floor previously matched nothing, so "an undead that rises
from the ground" was unfindable.

One bug found in its own work: casts_to_json emitted a doubled closing brace —
malformed JSON that still looked fine in a log. Fixed with a structural test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 11:43:42 +02:00

108 lines
5.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Makepad Arcade
Networked AI game sandbox (plan: repo-root game.md). Run with:
cargo run -p makepad-arcade
## Stock assets
Arcade ships with a searchable library of CC0 models and sounds. The binaries
are **not** vendored in git — fetch them once:
./apps/arcade/download_assets.sh # core packs, ~75 MB
./apps/arcade/download_assets.sh --packs=all # everything, ~185 MB
./apps/arcade/download_assets.sh --list # show packs, download nothing
The default fetches a **core** set (~1900 models) so a fresh clone isn't forced
to pull the lot; `--packs=all` gets the full Kenney 3D catalogue (**4669 models
across 47 packs**), and `--packs=nature-kit,car-kit` picks specific ones. Add
the 556 sounds and the nine rigged KayKit characters and the full library is
~5000 searchable assets — of which **36 are rigged and animated**.
Everything is gitignored and pinned — starter kits to exact GitHub commits, the
rest to content-hashed kenney.nl URLs — and every file is sha256-verified, so a
moved or tampered upstream fails loudly instead of silently changing the
library. Downloads are sequential with a delay: this is someone else's
bandwidth.
### Mirroring
`resources/MIRROR.toml` lists every pack with its canonical URL, sha256, size
and model count. Because these assets are CC0, anyone may re-host them, and
that file is what makes a mirror reproducible and verifiable. Point at one with:
ARCADE_ASSET_MIRROR=https://your.host/assets ./apps/arcade/download_assets.sh
./apps/arcade/download_assets.sh --mirror=https://your.host/assets
A mirror is expected to serve `<base>/<slug>.zip`. **The sha256 from
MIRROR.toml is verified identically whichever host served the bytes** — a
mirror is never trusted more than upstream; the hash is the authority.
Everything degrades gracefully without them: the demo runs with primitive
shapes, and tests that need real assets skip with a hint.
**Audio is Ogg Vorbis.** Every Kenney audio pack ships `.ogg` only — no WAV
variant exists upstream — and this tree has no vorbis decoder. The sounds are
therefore indexed and searchable by the AI but **not yet playable**; adding a
decoder (or running `download_assets.sh --transcode`, which converts to WAV
when ffmpeg is installed) is what closes that gap.
### Finding assets
The library is queried by *description*, never by filename, via
`makepad-game-assets`. The agent gets a `find_model` tool and a one-paragraph
summary — it searches, it never receives the catalogue (5000 entries would not
fit in a prompt, and the summary stays ~480 characters however large the
library grows). Ids look like `kenney/racing/vehicle-truck-yellow` and are
stable across re-downloads, because generated game code writes them.
Findability is built in three layers so it scales past 4000 models:
per-pack theme curation (~55 rows, giving every model its setting), filename
token parsing (free, and Kenney's names are systematic), and a hand-curated
query-time synonym table (~240 rows) that applies to the whole catalogue at
once. Item-level curation is spent only on the few hundred most-requested
things. At the full 4,999-entry catalogue: index build ~120 ms, a search
~0.2 ms, ~2.1 MB of heap (release).
Query-side stemming means an inflected request still reaches a base-form
alias ("smashing" → the `smash` alias), and when two entries tie on score the
kind the query implies wins — an unqualified noun like "spaceship" is an
object request, while "metal clang" or "win music" wants something audible.
## Credits
Every asset here is CC0 (public domain). Attribution isn't required by the
licence — we credit anyway, because these libraries exist because someone chose
to give them away.
- **Kenney** — <https://kenney.nl/assets> — 47 model packs totalling ~4,670
models (nature, city, castle, space, food, furniture, vehicles, dungeons,
characters and more), the five starter kits, and all seven sound packs
(impact, interface, sci-fi, music jingles, UI, RPG and digital audio). Thank
you for the extraordinary breadth of free, consistent, genuinely usable game
assets — this library is most of what Arcade can build with.
- **KayKit / Kay Lousberg** — <https://kaylousberg.itch.io/> — nine rigged and
animated characters: five adventurers (Knight, Barbarian, Mage, Rogue,
Rogue_Hooded) and four skeletons (Warrior, Mage, Rogue, Minion). All nine
share one 41-joint skeleton, so an animation authored for any of them plays
on every one — the adventurers ship 76 clips and the skeletons those same 76
plus 19 undead extras. Thank you for giving away rigs and animation sets,
which are the expensive part.
### Rigged characters
`find_cast` lists the animated casts. A cast is a set of characters sharing one
skeleton, so any state works on any member and a part can be recast without
touching animation code:
| rig | members | clips | notable states |
|-----|---------|-------|----------------|
| 41 joints (KayKit) | 9 | 7695 | block, dodge, hurt, dance, sleep, carry |
| 7 joints (Kenney mini) | 22 | 2532 | walk, run, jump, attack, sit, wave |
| 6 joints (Kenney platformer) | 5 | 25 | walk, run, jump, attack |
Pick members from ONE cast for a scene, and use different members so a crowd
isn't clones — the same rule that applies to props.
Machine-readable attribution lives in `resources/CREDITS.toml` so a published
game package can carry credit with it.