feat(ui): T2 框材组 8 件换皮接线批(样张 v2 冻结色板量化)——UiKit 单点工厂+五视图接线
♻ 复用件审阅+量化改色(tools/quantize_ui_skin.py,全 CC0,闸①色板 token 渐变带重映射/闸②token 天然合规/闸③图标四形互异/闸④授权干净):panel_stone/button_stone 两态/backdrop_stone/border_stone/营地图标四件(钥石金+柜台暖白)九件入 game/assets/art/ui/(PNG 不入 git,缺位 load_tex null 回退)。 接线=UiKit 单点:panel_skinned(B 区舞台 tint PANEL_STAGE/C 区主面板/图例面板+墨字随行改色 §7.2)+_apply_button_skin(全站按钮两态皮+墨字,UiKit.button 消费点零改)+frame_skinned(营地仪式石框容器)+icon_rect(营地钥石行/三柜台头图标,缺位纯文本回退)+开始界面石纹底噪底层(ui_assembler,z 序 dim 之上内容之下)。数据框/暗卡/结算回看保持素面或暗底(§7.1 数据素面纪律)。 ✦ B 区背景横幅不生图列缺料清单(归 T5 交付)。8 件=面板九宫格/按钮常态/按钮按压/石纹底噪/仪式石框/钥石图标/柜台图标组/B 区横幅(缺料)。轻闸=冒烟 557/0+四探针 PASS(带素材模式)+编辑器导入 9 件零报错。
This commit is contained in:
1 parent
f9b0765412
commit
0ad16747fc
7 files changed
+244
-12
No files matched your search
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""UI 框材组量化改色脚本(M-1 迭代 2 T2,2026-09-28)。
|
||||
|
||||
♻ 复用件 → 复用闸①色板量化:全部产物按明度重映射到 art-bible §4.1/§7.2 token 渐变带,
|
||||
不保留原包杂色;闸②禁入带=token 色天然合规;闸③灰度可辨=图标四形互异;闸④授权=全 CC0。
|
||||
源包 = artwork/ui-reuse-2026-09-23/(素材不入 git——先查本仓库,缺位回退主检出 D:/code/g-plan2):
|
||||
oga-kenney-pixel-ui-750(CC0,OGA) 9-Slice/Ancient 面板/按钮九宫格 48×48
|
||||
kenney-fantasy-ui-borders(CC0) panel-border 石框 48×48
|
||||
5-石纹底噪/stone_6.png(CC0,OGA Handpainted stone) 无缝底噪 1200×1200
|
||||
kenney-board-game-icons(CC0) 64px 图标四件
|
||||
产物 → game/assets/art/ui/(终稿素材目录,不入 git;消费 = UiKit.load_tex 守卫,缺位程序化回退)。
|
||||
用法:python tools/quantize_ui_skin.py
|
||||
"""
|
||||
from PIL import Image
|
||||
import os
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
MAIN_ROOT = "D:/code/g-plan2"
|
||||
SRC = ("artwork", "ui-reuse-2026-09-23")
|
||||
OUT = ("game", "assets", "art", "ui")
|
||||
|
||||
|
||||
def src_path(*parts):
|
||||
for base in (ROOT, MAIN_ROOT):
|
||||
p = os.path.join(base, *SRC, *parts)
|
||||
if os.path.exists(p):
|
||||
return p
|
||||
raise FileNotFoundError("/".join(SRC + parts))
|
||||
|
||||
|
||||
def out_path(name):
|
||||
p = os.path.join(ROOT, *OUT, name)
|
||||
os.makedirs(os.path.dirname(p), exist_ok=True)
|
||||
return p
|
||||
|
||||
|
||||
# §4.1/§7.2 token 渐变带(量化目标值——样张 v2 冻结色板)
|
||||
RAMP_PANEL = [(120, 106, 88), (226, 210, 182), (243, 234, 214)] # PANEL_BORDER→PANEL_DATA→PLATE 微亮
|
||||
RAMP_BUTTON = [(179, 154, 120), (201, 175, 131), (245, 234, 211)] # FLOOR→BRICK(掩尘"可按的砖")→PLATE
|
||||
RAMP_PRESSED = [(120, 106, 88), (179, 154, 120)] # PANEL_BORDER→FLOOR
|
||||
RAMP_BACKDROP = [(30, 25, 20), (120, 106, 88)] # BG_ROOT→PANEL_BORDER(低对比自然后退 §6.1)
|
||||
RAMP_BORDER = [(120, 106, 88), (201, 175, 131)] # PANEL_BORDER→BRICK
|
||||
ICON_GOLD = (240, 168, 40) # 奖励金(钥石)
|
||||
ICON_WARM = (240, 226, 200) # 暖白(柜台件,暗卡上可读)
|
||||
|
||||
|
||||
def ramp_color(ramp, t):
|
||||
if t <= 0:
|
||||
return ramp[0]
|
||||
if t >= 1:
|
||||
return ramp[-1]
|
||||
seg = t * (len(ramp) - 1)
|
||||
i = min(int(seg), len(ramp) - 2)
|
||||
k = seg - i
|
||||
a, b = ramp[i], ramp[i + 1]
|
||||
return tuple(int(a[j] + (b[j] - a[j]) * k) for j in range(3))
|
||||
|
||||
|
||||
def remap_luma(img, ramp, gamma=1.0):
|
||||
"""明度→token 渐变带重映射(保留 alpha 与纹理结构——闸①量化)。"""
|
||||
im = img.convert("RGBA")
|
||||
px = im.load()
|
||||
vals = [sum(px[x, y][:3]) / 3.0 for y in range(im.size[1]) for x in range(im.size[0])
|
||||
if px[x, y][3] > 8]
|
||||
lo, hi = min(vals), max(vals)
|
||||
span = max(1.0, hi - lo)
|
||||
for y in range(im.size[1]):
|
||||
for x in range(im.size[0]):
|
||||
r, g, b, a = px[x, y]
|
||||
if a <= 8:
|
||||
continue
|
||||
t = ((sum((r, g, b)) / 3.0) - lo) / span
|
||||
px[x, y] = ramp_color(ramp, t ** gamma) + (a,)
|
||||
return im
|
||||
|
||||
|
||||
def recolor_flat(img, rgb):
|
||||
"""剪影图标单色重着色(保留 alpha 抗锯齿——形状承载识别,闸③)。"""
|
||||
im = img.convert("RGBA")
|
||||
px = im.load()
|
||||
for y in range(im.size[1]):
|
||||
for x in range(im.size[0]):
|
||||
r, g, b, a = px[x, y]
|
||||
if a > 0:
|
||||
px[x, y] = rgb + (a,)
|
||||
return im
|
||||
|
||||
|
||||
def main():
|
||||
jobs = []
|
||||
# 面板/按钮九宫格(pixel-ui-750 Ancient;texture_margin 取 8px——消费侧同值)
|
||||
jobs.append(("panel_stone.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "oga-kenney-pixel-ui-750(CC0)", "9-Slice", "Ancient", "brown.png")), RAMP_PANEL)))
|
||||
jobs.append(("button_stone.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "oga-kenney-pixel-ui-750(CC0)", "9-Slice", "Ancient", "tan.png")), RAMP_BUTTON)))
|
||||
jobs.append(("button_stone_pressed.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "oga-kenney-pixel-ui-750(CC0)", "9-Slice", "Ancient", "tan_pressed.png")), RAMP_PRESSED)))
|
||||
# 石框(fantasy-ui-borders)
|
||||
jobs.append(("border_stone.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "kenney-fantasy-ui-borders(CC0)", "PNG", "Default", "Border", "panel-border-000.png")), RAMP_BORDER)))
|
||||
# 石纹底噪(seamless 1200→256,暗带低对比——B 区/开始层背景)
|
||||
stone = Image.open(src_path("5-石纹底噪", "stone_6.png")).convert("RGB").resize((256, 256), Image.LANCZOS)
|
||||
jobs.append(("backdrop_stone.png", remap_luma(stone, RAMP_BACKDROP, gamma=1.6)))
|
||||
# 营地图标四件(board-game-icons 64px;钥石=奖励金,柜台件=暖白)
|
||||
icon_dir = ("3-节点图与营地图标", "kenney-board-game-icons(CC0)", "PNG", "Default (64px)")
|
||||
jobs.append(("icon_keystone.png", recolor_flat(Image.open(src_path(*(icon_dir + ("suit_diamonds.png",)))), ICON_GOLD)))
|
||||
jobs.append(("icon_forge.png", recolor_flat(Image.open(src_path(*(icon_dir + ("sword.png",)))), ICON_WARM)))
|
||||
jobs.append(("icon_workshop.png", recolor_flat(Image.open(src_path(*(icon_dir + ("flask_full.png",)))), ICON_WARM)))
|
||||
jobs.append(("icon_atlas.png", recolor_flat(Image.open(src_path(*(icon_dir + ("book_open.png",)))), ICON_WARM)))
|
||||
|
||||
for name, im in jobs:
|
||||
im.save(out_path(name))
|
||||
print("saved:", "/".join(OUT + (name,)))
|
||||
print("=== 量化改色完成(%d 件,全 CC0;闸①②③④ 见脚本头注) ===" % len(jobs))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user