Files
brandling-safari/docs/CHARACTER_ART.md
Gabe 238cd9c6c3 Brandling Safari: LAN safari-zone creature catching on the HD-2D engine
- Authoritative 30 Hz server (ENet, SQLite): accounts, characters, wild AI, tall grass /
  water / wandering spawns, fishing, captures, follower chains, clock + weather, LAN beacon
- Client: title / join / character creator / play screens, prediction + reconciliation,
  interpolated players and parties, HD-2D UI (dialogue with Professor Henry, HUD, collection)
- Shared deterministic zone generator, movement, protocol, roster, appearance
- Paper-doll character art pipeline (PixelLab base walk + per-item animation edits)
- Brandling roster from the company list (logo colors, jev types), Blender safari props
- Headless test bot
2026-10-02 15:20:27 -05:00

4.4 KiB

Character art: one animation, any outfit

Player sprites are paper dolls. PixelLab is called a fixed number of times to build a layer library; every look a player can create is composed from it on the CPU with no further API calls.

flowchart LR
  A[create-character-with-4-directions<br/>bald, white tee, grey shorts, barefoot] --> B[walking-4-frames template<br/>south, east, north]
  B --> C[15 base frames<br/>3 dirs x stand + 4 walk]
  C --> D[edit-animation-v2 per item<br/>all 15 frames in one call,<br/>item drawn in pure green]
  C --> E[split base: skin / tee / shorts<br/>shade layers + fixed pixels]
  D --> F[extract green hue -> shade layer<br/>outline + details -> fixed pixels]
  E --> G[assets/characters/&lt;body&gt;/]
  F --> G
  G --> H[client composes any look:<br/>skin tone, item per slot, color per slot]

Why it works

  • The base walk is generated once per body. edit-animation-v2 edits all 15 frames in one request and keeps the motion, so an item's frames line up pixel-for-pixel with the base frames. No per-outfit animation calls.
  • Items are drawn in a stand-in color (bright pure green) that no base pixel uses. The extractor (tools/character_art.py build) keeps the green pixels as a shade layer: each pixel stores a shade index 1..4 (luminance relative to the frame's median, so frames the model rendered a bit brighter or darker still band the same). The dark outline and small details right next to the green become fixed pixels.
  • The base body is split the same way: skin, tee and shorts become shade layers, everything else (outline, eyes, mouth) stays fixed. Skin tone is therefore just a color, like a shirt.
  • Tops are clipped two rows below the shorts' waistline (the edit model sometimes turns a sweater into a robe).
  • West is east mirrored at runtime, so only three directions are stored.

Cost

Step Calls Generations
Base body (x2) create-character-with-4-directions ~1 each
Walk (x2) characters/animations template mode, 3 directions ~0 (template)
Item (x18 per body) edit-animation-v2, 15 frames 20 each
Professor Henry character + walk + create-image-pixen portrait ~2

18 items x 2 bodies = 720 generations for every combination of 5 hair styles, 3 hats, 4 tops (+ the base tee), 3 bottoms (+ the base shorts) and 3 shoes, in any color and any of 6 skin tones. A new item is one more call per body.

Files

art_src/characters/<body>/          raw PixelLab output (rotations, walk4/, items/<id>/frame_N.png)
assets/characters/manifest.json     slots, z order, skin tones, palette, items, bodies, NPCs
assets/characters/<body>/body.png   fixed pixels of the base body (sheet)
assets/characters/<body>/skin_shade.png, top_tee_shade.png, bottom_shorts_shade.png
assets/characters/<body>/<item>.png + <item>_shade.png
assets/characters/henry/henry.png + portrait.png

Sheets are 5 columns (standing, walk 0..3) x 3 rows (south, east, north) of 64 px cells. Shade layers store the shade index in red (index * 60) with alpha 255.

Composition

client/CharacterSprite.cpp (C++) and tools/character_compose.py (Python, for previews) implement the same steps:

  1. skin shade layer in the skin tone,
  2. tee and shorts shade layers (always under everything; recolored when they are the chosen top / bottom),
  3. body fixed pixels,
  4. every chosen item by slot z order (shoes < bottom < top < hair < hat): shade layer in the chosen color, then its fixed pixels.

A color becomes 4 shades multiplicatively (colorRamp / ramp): x0.50 with a cool tint, x0.73, the color itself, and a warm highlight 34% toward cream. Keep both implementations in sync.

Commands

python3 tools/character_art.py bases          # base bodies + walk (cached)
python3 tools/character_art.py items [ids..]  # missing item edits (resumable)
python3 tools/character_art.py henry          # Professor Henry + portrait
python3 tools/character_art.py build          # extract layers, write the manifest (free)
python3 tools/character_art.py preview        # contact sheets in art_src/preview/
python3 tools/pixellab_client.py              # ledger spend + account balance

Paid jobs are recorded in art_src/.pending/ before waiting, so an interrupted run resumes the same jobs instead of paying again. To add an item, add it to ITEMS in tools/character_art.py, run items <id> and build.