fighting-konoha/guide/sprite-qa.md

9.3 KiB
Raw Permalink Blame History

Sprite QA — supaya tidak ada frame yang besar sendiri

Mata manusia baru sadar frame "berdenyut" setelah dimainkan. Jadi sprite diukur dulu sebelum masuk game. Semua pemeriksaan dilakukan pada atlas final (sprite-sheet-alpha.png + manifest.json), bukan frames/.

Untuk proyek ini baca character-workflow.md terlebih dahulu. Angka toleransi di bawah adalah target untuk landmark yang valid. Hasil template yang menangkap bahu/sepatu atau gagal karena rotasi harus dicatat sebagai tidak terukur; jangan dilaporkan lolos hanya berdasarkan median lain.

Tool ada di tools/. Jalankan dengan Python dari venv sprite-gen (butuh Pillow, NumPy, sprite_gen):

PY=<SPRITE_GEN_ROOT>/.venv/bin/python        # Windows: .venv\Scripts\python.exe, set PYTHONUTF8=1

Checklist (wajib lolos semua)

Skala antar gerakan — SEMUA gerakan, termasuk serangan, lompat, skill

  • measure_head_scale.py dijalankan untuk setiap gerakan terhadap idle frame 0: median tiap gerakan 0.97–1.03
  • head-check.png dilihat: semua kepala tampak sama besar dengan TEMPLATE; frame yang kotaknya bukan kepala dicatat sebagai "tidak terukur" dan dicek dengan perbandingan badan penuh (lihat langkah 2)
  • Setiap menambah gerakan baru, cek ini diulang untuk gerakan itu (jangan anggap aman karena gerakan lain sudah lolos)

Frame sambungan — frame yang bertemu idle (awal & akhir serangan/kombo, akhir hurt)

  • Ukur badan penuh dan crown→sabuk→kaki, bukan kepala/rambut saja. Pada ARCO, kepala seragam pernah menyembunyikan recovery 170–176 px dibanding idle 180 px.
  • Frame pertama dan terakhir tiap gerakan aksi diukur sendiri terhadap idle frame 0 di atlas (bukan cuma median strip): kepala 0.97–1.03, dan untuk pose tegak tinggi helm & helm→sabuk selisih ≤ 3 px
  • Posisi kepala frame terakhir sama dengan idle (± 2 px) — kalau tidak, geser bulat (write_dx.py)
  • Dilihat berdampingan: frame terakhir | idle f0 dengan garis tinggi yang sama (known-issues #17)

Per gerakan tegak (idle, walk, run)

  • Tinggi frame idle selisih ≤ 2 px satu sama lain
  • Posisi badan (torso_x) tidak goyang lebih dari ±1.5 px dalam satu gerakan
  • Naik-turun badan saat jalan (bob) wajar, bukan hasil skala yang berbeda
  • Walk/run: kaki bergantian — measure_atlas.py baris "jarak kaki" lebar→rapat→lebar→rapat, rasio terkecil/terbesar ≤ 0.45 (kalau tidak: generate ulang, lihat known-issues #15)
  • Walk/run di game: frame maju berdasarkan jarak tempuh (stride), bukan fps tetap

Makhluk terbang / tanpa kaki

  • Bagian yang seharusnya diam (kepala saat hover, badan saat menyerang) tidak bergeser > 2 px antar frame
  • Tidak ada piksel di tepi sel (sayap/ekor terpotong)

Semua gerakan

  • Jump hanya memakai pose atletis yang dipilih; tidak replay crouch di udara. Tuck doublejump tidak memiliki kaki menendang.
  • Normal jump → tuck → spin → kembali jump dilihat di runtime dengan pivot terukur dan skala konstan.
  • Source, request, jumlah frame, manifest, wrapper JS, metrics, dan titik efek sama-sama versi terbaru.
  • Gerakan baru dibuat dengan base dari kartu karakter yang sama (bukan base baru / deskripsi teks saja)
  • Kaki semua frame di baris yang sama (bottom sama) — untuk karakter yang berdiri
  • Tidak ada frame kosong / terpotong / pose menempel ke tetangga
  • Tidak ada sisa warna chroma (pinggiran magenta/hijau) — lihat di atas latar gelap dan terang
  • Ketajaman sama di semua frame (tidak ada frame yang sedikit buram) — artinya tidak ada scale ≠ 1 atau dx pecahan di curation.json
  • Identitas konsisten: warna, senjata di tangan yang sama, aksesori tidak hilang
  • GIF preview tiap gerakan dilihat sekali dengan kecepatan asli dan 0.5×

Terakhir

  • qa-notes.md diisi: faktor skala, koreksi per pose, dx, hasil measure_atlas.py
  • Pisahkan bukti angka, contact sheet/screenshot, dan loop yang benar-benar ditonton. Jangan menyebut full motion sudah mulus bila baru keyframe yang diperiksa.
  • Jika menggunakan tool landmark putih ARCO untuk karakter berambut gelap/bertanduk, ganti landmark; jangan membiarkan alat memilih armor sebagai rambut.

Langkah pemeriksaan

1. Ukur atlas

$PY guide/tools/measure_atlas.py run

Contoh keluaran yang baik:

      idle 0: h=141 bottom=199 head_w=38 torso_x=112.0 ok
      idle 1: h=140 bottom=199 head_w=40 torso_x=111.6 ok
      ...
              rentang tinggi 140–141  rentang torso_x 111.6–112.3
SEMUA LOLOS

Contoh yang buruk (kasus nyata sebelum diperbaiki): idle frame 1 h=149 sementara lainnya 142 → karakter terlihat membesar sekali tiap siklus napas.

2. Cek skala SEMUA gerakan lewat kepala (di gambar asli)

Aturan: samanya ukuran pixel palsu (pitch) TIDAK berarti samanya ukuran karakter. Model bisa menggambar strip serangan dengan karakter lebih kecil (supaya pedang terangkat muat di slot), dengan pitch yang sama. Jadi setiap gerakan diukur terhadap idle memakai bagian yang bentuknya tetap: kepala/helm.

Simpan --rel yang sedang dipakai di rel.json (satu sumber kebenaran, juga dicatat di kartu karakter):

{ "idle": [1, 0.95, 1, 0.987], "walk": [0.990], "jump": [1.032],
  "attack1": [1.059], "attack2": [1.075], "attack3": [0.974] }
$PY guide/tools/measure_head_scale.py <char_dir> --poses idle:4,walk:6,jump:4,attack1:4,attack2:4,attack3:4 \
    --rel-json rel.json --landmark white [--head-box x0,y0,x1,y1]
   attack1 0: kepala di atlas = 0.878 × idle  (skor 0.45)
   ...
              median 0.944 → PERLU KOREKSI; --rel baru 1.059
  • Pengukuran di gambar asli (kepala ±120 px) dengan pencocokan tepi — di atlas kepala cuma ±20 px dan model memberi pencahayaan berbeda per strip, jadi pencocokan warna/siluet di atlas tidak bisa dipercaya.
  • --head-box: kotak kepala di pose acuan (lihat tile TEMPLATE di head-check.png; harus berisi kepala saja).
  • --landmark white: untuk karakter dengan bulu helm/rambut putih, mencegah kepala tertukar dengan bahu.
  • Wajib buka head-check.png. Tiap kepala dikembalikan ke ukuran template memakai skala terbaca; kalau benar, semuanya sama besar. Kotak yang bukan kepala = "TIDAK VALID" → frame itu dinilai dengan median gerakannya, lalu dicek manual dengan perbandingan badan penuh berjajar (kaki di garis yang sama).
  • Koreksi per gerakan (satu angka untuk satu strip), bukan per frame, kecuali frame idle yang jelas beda.

Jangan pakai registrasi siluet (register_frames.py tanpa --dx-only) untuk menilai skala pose aksi: lutut ditekuk menurunkan kepala dan alat "membesarkan" karakter untuk mengejarnya; lengan/senjata juga mengacaukan kecocokan. register_frames.py hanya untuk mencari geser bulat pada pose tegak.

Perbaiki skala di gambar asli

# satu angka --rel = strip diperkecil utuh (aman walau pose di gambar asli saling menempel)
$PY guide/tools/rebuild_raw.py raw-original/attack1.png run/raw/attack1.png --poses 4 --rel 1.059
# angka per pose (khusus frame yang beda sendiri, mis. idle)
$PY guide/tools/rebuild_raw.py raw-original/idle.png run/raw/idle.png --poses 4 --rel 1,0.95,1,0.987
# pose saling menempel juga bisa: dipotong di titik sentuh, hanya pose dengan rel berbeda yang ditempel ulang
$PY guide/tools/rebuild_raw.py raw-original/combo3.png combo-run/raw/combo3.png --poses 6 --rel 1.143,1.143,1.143,1.143,1.143,1.223
sprite-gen extract --run-dir run --states idle,attack1
  • --rel di rel.json selalu relatif terhadap gambar asli (bukan terhadap raw sebelumnya). measure_head_scale.py sudah memperhitungkan rel yang sedang dipakai dan mencetak --rel baru.
  • Setelah extract ulang → ukur lagi sampai semua median 0.97–1.03 → ukur ulang hitbox & titik efek di game (lihat sprite-runtime.md).

Perbaiki goyang dengan geser bulat

$PY guide/tools/register_frames.py run walk --dx-only   # → dx bulat: 1,-5,-4,0,0,-4
$PY guide/tools/write_dx.py run walk 1,-5,-4,0,0,-4
sprite-gen compose-atlas --run-dir run
$PY guide/tools/measure_atlas.py run                    # harus SEMUA LOLOS

Geser bulat = piksel dipindah utuh, tanpa blur (sudah diverifikasi: selisih piksel ≤ 2/255).

Yang TIDAK boleh dilakukan

Jangan Kenapa
Koreksi ukuran dengan scale di curation.json Di-resample bicubic → frame itu sedikit buram → berkedip tajam/buram saat animasi
dx/dy pecahan (0.5, 1.5) Sama: resample → buram sebelah
Mengecilkan tiap frame agar muat sel Tiap frame dapat skala berbeda → karakter berdenyut. Perbesar sel
Memeriksa di frames/ Belum termasuk curation; yang dipakai game adalah atlas
Menilai hanya dengan mata di gambar diam Selisih 2 px tidak terlihat di atlas, tapi terasa saat animasi
Menyembunyikan pose jelek dengan mengubah fps Masalah tetap ada; generate ulang strip itu
Menganggap strip "sudah sama skala" karena pitch-nya sama Pitch sama ≠ karakter sama besar; serangan bisa 6–15 % lebih kecil
Hanya mengecek idle & walk Justru serangan/skill yang paling sering meleset (pose senjata terangkat)
Percaya angka alat tanpa membuka head-check.png Alat bisa menangkap bahu/lambang sebagai "kepala" dan tetap mencetak angka