- 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
92 lines
4.4 KiB
Markdown
92 lines
4.4 KiB
Markdown
# 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.
|
|
|
|
```mermaid
|
|
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/<body>/]
|
|
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
|
|
|
|
```sh
|
|
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`.
|