Files
gplan-2/tools/standee_asset_check.py
T
DKChan 4271216b73 feat(t1): 素材管线正式化——pixelize_candidates.py --fixer+九张 48px 站桩槽位档生成器+机检脚本
①--fixer: rust pixelfixer(D:/code/pixel-art-fixer,MIT)收网格(网格检测+重建)+封闭灰袋清除收编(三闸=近底色∧中性灰∧组件不触图缘,防误伤盔甲与影;09-28 手工批次正式化,遗留项清偿)
②--standee48: 九张 48px 槽位档(八敌 pixel-1+主角 normal_1x→character_enemy_*/character_hero_portrait)——统一比例最大体量归 48(相对体量语义=字形回退同构)/底中锚脚贴底缘/块内众数投票降采样(零插值,输出色⊂源色板;NEAREST 直缩 0.25x 糊,goblin A/B 实证,代拍板待追认)/超宽按宽收兜底
③tools/standee_asset_check.py 机检: 高 48/宽≤75/底锚/≤32 色/alpha 二值 五断言,必需集=八敌+主角三态(attack/hit 缺位=T3 范围暂 MISS)
产物九张已落 worktree game/assets/art/character/(gitignored 不入库);机检 9/9 过;轻闸=冒烟 576/0+三探针 PASS 零退化
2026-09-29 03:59:27 +08:00

80 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""站桩槽位素材机检(M-3 迭代 6 T1——48px 档契约断言;重闸素材面闸)。
四断言(契约=计划书 C4/圣经 §8.2):
尺寸 高恒 48(槽位源档 ×2 整数放大=盒高 96);宽 1..75(盒宽 150 半值)。
锚点 底行有不透明像素(底中锚——纵向比例自底边口径,脚贴画布底缘)。
色数 ≤32(fixer 批 32 色冻结;NEAREST 采样不产生新色)。
无插值 alpha 二值(仅 0/255——双线性/混合会在边缘产生中间 α)。
用法: python tools/standee_asset_check.py [角色素材目录]
默认 game/assets/art/character/;必需集=八敌+主角 portrait/attack/hit。
"""
import os
import sys
from collections import Counter
from PIL import Image
REQUIRED = ["character_enemy_%s.png" % k for k in
("bugbear", "drider", "duergar", "duergar_king", "giant_spider",
"goblin", "hook_horror", "ogre_chief")] + \
["character_hero_portrait.png", "character_hero_attack.png", "character_hero_hit.png"]
SLOT_H = 48
W_MAX = 75
COLOR_MAX = 32
def check(path):
problems = []
im = Image.open(path).convert("RGBA")
w, h = im.size
if h != SLOT_H:
problems.append("高应 48,实得 %d" % h)
if not (1 <= w <= W_MAX):
problems.append("宽应 1..%d,实得 %d" % (W_MAX, w))
px = im.load()
colors = set()
alphas = set()
bottom_opaque = 0
for y in range(h):
for x in range(w):
c = px[x, y]
alphas.add(c[3])
if c[3] > 0:
colors.add(c[:3])
if y == h - 1:
bottom_opaque += 1
if bottom_opaque < 1:
problems.append("底行无不透明像素(底中锚失守——脚未贴画布底缘)")
if len(colors) > COLOR_MAX:
problems.append("色数 %d > %d" % (len(colors), COLOR_MAX))
if not alphas <= {0, 255}:
problems.append("alpha 非二值(插值痕迹:%s)" % sorted(alphas - {0, 255})[:5])
return problems, (w, h, len(colors))
def main():
root = sys.argv[1] if len(sys.argv) > 1 else os.path.join("game", "assets", "art", "character")
missing = [f for f in REQUIRED if not os.path.isfile(os.path.join(root, f))]
n_fail = 0
for f in REQUIRED:
p = os.path.join(root, f)
if not os.path.isfile(p):
print("MISS %-36s 缺位" % f)
n_fail += 1
continue
problems, info = check(p)
if problems:
n_fail += 1
for pr in problems:
print("FAIL %-36s %s" % (f, pr))
else:
print("PASS %-36s %dx%d 色数%d" % (f, info[0], info[1], info[2]))
print("机检完成:%d 件必需,%d 件未过" % (len(REQUIRED), n_fail))
sys.exit(1 if n_fail else 0)
if __name__ == "__main__":
main()