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
91
docs/CHARACTER_ART.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 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`.
|
||||
BIN
docs/catch.jpg
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
docs/collection.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
docs/creator.jpg
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
docs/dock.jpg
Normal file
|
After Width: | Height: | Size: 249 KiB |
BIN
docs/dusk.jpg
Normal file
|
After Width: | Height: | Size: 242 KiB |
BIN
docs/henry.jpg
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
docs/night.jpg
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
docs/title.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |