9.8 KiB
Sprite Pipeline
Untuk proyek fighting ini, mulai dari character-workflow.md dan template request: 14 state/50 frame, cell 320×288, jump/tuck masing-masing1 pose. Contoh generik di bawah harus mengikuti kontrak tersebut saat dipakai di proyek ini.
Alur dari nol sampai atlas siap pakai. Contoh memakai skill sprite-gen (sprite-gen prepare/extract/compose-atlas)
dan model gambar yang tidak bisa membuat strip super lebar (mis. GPT Image, maksimal 21:9). Prinsipnya berlaku
juga untuk alat lain.
1 base ──► 2 prepare ──► 3 generate strip ──► 4 normalisasi skala ──► 5 extract
▲ │
└── 7 koreksi per pose ◄┘ 6 ukur
│
8 geser bulat (curation) ──► 9 compose atlas ──► 10 QA
1. Buat & kunci karakternya dulu — baru sprite
Jangan langsung minta sprite sheet. Urutannya selalu:
- Buat satu gambar karakter (base) sampai benar-benar disetujui: desain, warna, proporsi, gaya.
- Simpan sebagai kartu karakter (di bawah).
- Baru setelah itu generate strip gerakan — setiap strip melampirkan base ini sebagai referensi.
Alasannya: model gambar mengikuti referensi jauh lebih konsisten daripada mengikuti deskripsi teks. Kalau desain belum dikunci, tiap strip akan "menafsirkan ulang" karakternya (warna, helm, senjata berubah), dan itu tidak bisa diperbaiki di tahap mana pun setelahnya.
- Generate 2–4 varian, pilih satu. Semua gerakan nanti mengacu ke gambar ini.
- Syarat base:
- full body, tidak terpotong (termasuk senjata, jubah, bulu helm)
- menghadap kanan (kiri didapat dengan flip di game)
- background satu warna flat (chroma key) yang tidak dipakai karakter — lihat bagian Chroma
- pose netral/idle, proporsi final
- Simpan:
base/base-a.png(+ job id generator bila perlu dipakai sebagai referensi).
Chroma key: pilih warna yang jauh dari warna karakter.
- Karakter biru/perak/emas/hijau → magenta
#FF00FF - Karakter merah/pink/ungu → hijau
#00FF00 - Larang warna itu di prompt ("do not use magenta/pink/purple on the character").
Kartu karakter
Simpan di <karakter>/character.md (atau json) dan pakai ulang setiap kali menambah gerakan baru,
walaupun berbulan-bulan kemudian. Gerakan baru yang dibuat tanpa kartu ini hampir pasti tidak cocok.
# Kartu karakter: knight
- Base: base/base-a.png (generator job id: b1469b8f-…, bisa dipakai langsung sebagai referensi)
- Prompt base: <salin prompt lengkap yang menghasilkan base>
- Model: gpt_image_2_5, variant sunburst, quality high, resolution 2k
- Palet: silver steel, gold trim, royal blue tabard & cape, white plume, brown leather
- Chroma key: #FF00FF Menghadap: kanan
- Skala: target/pitch diukur untuk idle ±180 px; kepala DAN badan dibandingkan; rel.json milik karakter ini
- Sel: 320×288, margin x=10/y=8, anchor kaki (160, 280)
- Gerakan: gunakan 14 state pada template; walk/run 4, jump/tuck 1, attack1-3 4
Karakter kedua dan seterusnya (supaya satu game satu gaya)
Base karakter baru dibuat dari deskripsi gaya bersama tanpa gambar karakter lama. Sesuai revisi pengguna, label STYLE ONLY belum cukup mencegah kemiripan. Prompt wajib menentukan siluet, rambut, wajah, palet, kostum, anatomi/alat, dan aksesori baru serta menyebut ciri roster lama yang harus dihindari. Lihat aturan pembeda. Setelah base baru disetujui, kartu dan base miliknya sendiri menjadi referensi identitas untuk seluruh strip.
Karakter yang jauh lebih besar (bos, naga) boleh punya target skala lebih besar (pixel sedikit lebih tebal)
supaya tampil besar di layar — tulis di kartunya.
2. Siapkan run
Default proyek: empat frame untuk sebagian besar gerakan, satu pose atletis untuk jump dan satu pose tuck untuk doublejump. Enam frame walk adalah opsi pengembangan yang harus mengubah request/guide/QA bersama, bukan default tersembunyi. Berikut cuplikan; template menyediakan semua 14 state.
{
"cell": { "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8 },
"states": {
"idle": { "frames": 4, "fps": 5, "loop": true },
"walk": { "frames": 4, "fps": 10, "loop": true },
"jump": { "frames": 1, "fps": 10, "loop": false },
"doublejump": { "frames": 1, "fps": 12, "loop": false },
"attack1": { "frames": 4, "fps": 13, "loop": false }
}
}
sprite-gen prepare --out-dir run --character-id hero --base-image base/base-a.png \
--request request.json \
--chroma-key "#FF00FF" --no-fit-pixel-unfake --fit-align-x foot-centroid --fit-align-y bottom
Ukuran sel: harus muat pose terbesar (pedang terangkat, tusukan maksimal) di skala final tanpa dikecilkan. Hitung setelah langkah 4; kalau ragu, lebih besar lebih aman. Frame yang tidak muat akan dikecilkan otomatis per frame → ukuran karakter jadi tidak konsisten.
pixel-unfake mati bila gambar AI bukan pixel art asli (grid tidak terdeteksi). Tanda: extractor melaporkan
pitch disagreement … detect 1.00x1.00.
3. Generate strip per gerakan
- Satu strip = satu gerakan, N pose dari kiri ke kanan.
- Referensi yang dilampirkan: (1) base, (2) layout guide berisi N kotak.
- Model dengan rasio terbatas: buat ulang layout guide di rasio yang didukung (mis. 21:9) dengan jumlah kotak sama. Extractor memisahkan pose berdasarkan bentuk, jadi strip tidak harus persis rasio sel.
- Prompt: lihat sprite-prompts.md.
- Simpan hasil asli tanpa diubah di
raw-original/<state>.png. Ini sumber untuk semua koreksi berikutnya.
Simpan pula versi immutable <state>-v1.png, <state>-v2.png, dan seterusnya. <state>.png boleh menjadi salinan sumber terpilih untuk tool; jangan me-resample file versi aslinya. Jump/tuck satu pose memakai 1:1 dan --poses 1, bukan strip empat pose.
Periksa cepat dengan mata: jumlah pose benar, tidak saling menempel, tidak ada efek lepas (garis gerak, debu). Kalau gagal, generate ulang strip itu saja.
4. Normalisasi skala antar strip
Model menggambar tiap strip dengan ukuran berbeda (contoh nyata: idle 860 px tinggi, walk 675 px). Langkah pertama: samakan pitch (ukuran pixel palsu) tiap strip. Ini hanya perkiraan awal — model bisa menggambar karakter lebih kecil dengan pitch yang sama (terutama strip serangan). Skala sebenarnya dipastikan di langkah 6 lewat ukuran kepala.
from PIL import Image
from sprite_gen.frames.extract import estimate_pixel_grid_runlen
im = Image.open("raw-original/walk.png").convert("RGB")
px, py = estimate_pixel_grid_runlen(im.convert("RGBA"))
factor = TARGET / ((px + py) / 2) # TARGET mis. 0.5 → 1 px final = 2 pixel palsu
TARGET menentukan seberapa "kotak-kotak" hasil akhirnya: 0.5 = detail halus (hi-bit), 0.33 = lebih chunky.
5. Extract
Tulis strip hasil normalisasi ke run/raw/<state>.png, lalu:
sprite-gen extract --run-dir run
Harus errors: []. Warning native logical exceeds the physical cap = sel kekecilan → perbesar sel (langkah 2).
6. Ukur
Jalankan pengukuran di sprite-qa.md untuk setiap gerakan — termasuk serangan, lompat, dan pose yang dipakai skill. Yang dicari:
- skala tiap gerakan relatif terhadap idle frame 0 lewat ukuran kepala (
measure_head_scale.py, di gambar asli). Pitch yang sama tidak menjamin karakter sama besar. - posisi horizontal badan (
torso_x) per frame - tinggi badan/recovery, crown→sabuk dan sabuk→kaki pada baseline yang sama. Kepala/rambut seragam saja pernah menyembunyikan tubuh ARCO yang mengecil.
Hasil pencocokan kepala yang menangkap sepatu/bahu atau terhalang alat adalah tidak valid. Buka montage, pilih landmark sesuai karakter, lalu lakukan perbandingan badan penuh. Jangan menerapkan faktor skala besar dari hasil yang salah atau menyalin faktor ARCO ke karakter lain.
7. Koreksi skala per pose — di gambar asli
Kalau ada gerakan yang median skalanya di luar 0.97–1.03 (atau satu pose yang jelas beda): bangun ulang
run/raw/<state>.png dari raw-original/
dengan faktor per pose = factor_strip × koreksi_pose, satu kali resample (BOX). Script: lihat
sprite-qa.md → Perbaiki skala. Lalu extract ulang gerakan itu.
Catat semua nilai --rel di rel.json karakter. Contoh nyata (knight):
idle [1,0.95,1,0.987], walk 0.990, jump 1.032, attack1 1.059, attack2 1.075, attack3 0.974.
8. Sejajarkan dengan geser bulat
Setelah skala benar, tulis curation.json hanya berisi geser integer (dx, bila perlu dy), scale = 1.
Hitung dx dengan registrasi badan atas ke idle frame 0 (script di sprite-qa.md).
9. Compose
sprite-gen compose-atlas --run-dir run
sprite-gen compose-gif --run-dir run --out-dir run/previews
Output: sprite-sheet-alpha.png + manifest.json (+ GIF preview per gerakan).
10. QA & kirim ke game
- Checklist sprite-qa.md harus lolos pada atlas final.
- Salin atlas + manifest ke folder aset game. Jangan pernah mengambil dari
run/frames/(belum ter-curation). - Catat di
run/qa-notes.md: provider, faktor skala, koreksi per pose, dx, hasil ukur.
Struktur folder yang disarankan
<karakter>/
base/ base-a.png (identitas)
raw-original/ strip asli dari generator — tidak pernah diedit
guides/ layout guide rasio generator
request.json
run/ run sprite-gen (raw/, frames/, curation.json, atlas, manifest, qa-notes.md)
Windows
- Set
PYTHONUTF8=1sebelum memanggilsprite-gen(help/log memakai karakter unicode). - Venv Windows ada di
.venv\Scripts; bila dokumen menulis.venv/bin, buat junctionbin → Scripts.