From d76a023fa9701413d7ed29f5509310648ec833e4 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Wed, 16 Sep 2026 20:55:11 +0800 Subject: [PATCH] =?UTF-8?q?chore(art):=20=E4=B8=BB=E8=A7=92=E5=83=8F?= =?UTF-8?q?=E7=B4=A0=E6=A0=B7=E4=BE=8B=E4=B8=89=E4=BB=B6=E7=95=99=E5=AD=98?= =?UTF-8?q?=E2=80=94=E2=80=94=E7=BE=8E=E6=9C=AF=E7=B4=A0=E6=9D=90=E6=94=B9?= =?UTF-8?q?=E8=B5=B0=20ComfyUI=20=E6=8B=8D=E6=9D=BF=E6=89=B9=E4=BA=A7?= =?UTF-8?q?=E7=89=A9=EF=BC=88=E7=AC=AC58=E6=9D=A1=E6=94=B6=E5=8F=A3?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design/art/samples/generate_hero_sample.py | 128 ++++++++++++++++++ .../samples/hero-standing-pose-preview.png | Bin 0 -> 4904 bytes design/art/samples/hero-standing-pose.png | Bin 0 -> 540 bytes 3 files changed, 128 insertions(+) create mode 100644 design/art/samples/generate_hero_sample.py create mode 100644 design/art/samples/hero-standing-pose-preview.png create mode 100644 design/art/samples/hero-standing-pose.png diff --git a/design/art/samples/generate_hero_sample.py b/design/art/samples/generate_hero_sample.py new file mode 100644 index 00000000..e8a15757 --- /dev/null +++ b/design/art/samples/generate_hero_sample.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +""" +主角站姿像素画样例生成器 — Dungeon Sweeper +依据 design/art/art-bible.md: + - 小人蓝 #2E6E8E 主色(§4.1) / 亮色描边+落影底盘轮廓通道(§3.1) + - 3 头身候选档(§3.1 两档之一;终稿归 §5 角色批) + - 窄/直立/对称/凹凸≤2,剑收于剪影内(§3.1/§3.4) + - 不用红(危险专属)/金(收益专属)/紫赤 300-345° 禁入带(§4.2) +输出: hero-standing-pose.png (1x) + hero-standing-pose-preview.png (8x 四联测试条) +""" +from PIL import Image, ImageOps + +PAL = { + 'O': (255, 246, 224, 255), # 亮色描边(暖白) + 'b': (85, 144, 175, 255), # 蓝高光(小人蓝同族) + 'B': (46, 110, 142, 255), # 小人蓝 §4.1 + 'd': (31, 74, 96, 255), # 蓝暗部(同族) + 'S': (242, 199, 156, 255), # 肤色(暖) + 's': (214, 159, 108, 255), # 肤暗部 + 'E': (51, 38, 27, 255), # 数字墨(眼) §4.1 + 'T': (138, 90, 52, 255), # 皮革(木色族 §4.4) + 't': (92, 58, 30, 255), # 皮革暗部 + 'W': (183, 195, 203, 255), # 剑-钢 + 'w': (132, 147, 157, 255), # 剑-钢暗部 + 'G': (110, 91, 62, 255), # 落影底盘(暖深) +} + +# 左半 16 列(col 0..15),右半水平镜像;-figure 对称剪影 +LEFT = [ + "................", # 0 + "................", # 1 + "................", # 2 + ".............OOO", # 3 兜帽顶 + "............OBBB", # 4 + "...........Obbbb", # 5 + "...........ObbBB", # 6 + "...........ObBBB", # 7 + "...........OSSSS", # 8 额/脸顶(加宽脸窗) + "...........OSSES", # 9 + "...........OSSES", # 10 眼(下移一档更亲和) + "...........OSSSS", # 11 + "...........OSsss", # 12 下巴暗 + "...........Odddd", # 13 兜帽收底 + "..........Oddddd", # 14 肩起 + "..........OdBBBB", # 15 肩 + "..........OdBbbB", # 16 胸(中高光) + ".........OBBBbbB", # 17 臂现 + ".........OdTBbbB", # 18 臂+背带 + ".........OdTBbbB", # 19 + ".........OdTBbbB", # 20 + ".........OdTBbbB", # 21 + ".........OdTBbbB", # 22 + ".........OdTBbbB", # 23 + ".........OdTttTW", # 24 腰带+扣(2px 居中) + ".........OdTttTW", # 25 + ".........OdTTTTt", # 26 双手套握柄 + ".........OddBBBt", # 27 柄下 + ".........OBBOWWW", # 28 剑格 + ".........OBBBBBW", # 29 髋/剑身起 + "..........OdBddW", # 30 腿+剑 + "..........OdBddW", # 31 + "..........OdBddW", # 32 + "..........OdBddW", # 33 + "..........OdBddW", # 34 + "..........OdBddW", # 35 + "..........OdBddW", # 36 + "..........OTTttW", # 37 靴 + "..........OTTttW", # 38 + "..........OTTttW", # 39 + "..........OTTttW", # 40 + "..........OttttW", # 41 靴底 + "..............OW", # 42 剑尖 + "................", # 43 + ".........GGGGGGG", # 44 落影底盘 + "...........GGGGG", # 45 + "................", # 46 + "................", # 47 +] + +W, H = 32, 48 +grid = [['.'] * W for _ in range(H)] +for y, lft in enumerate(LEFT): + for x, ch in enumerate(lft): + grid[y][x] = ch + grid[y][W - 1 - x] = ch + +# 剑身右侧 1px 暗部(col 16),避免完全对称的死平 +for y in range(29, 42): + if grid[y][15] == 'W': + grid[y][16] = 'w' + + +def render(scale=1): + img = Image.new('RGBA', (W, H), (0, 0, 0, 0)) + px = img.load() + for y in range(H): + for x in range(W): + c = PAL.get(grid[y][x]) + if c: + px[x, y] = c + return img.resize((W * scale, H * scale), Image.NEAREST) + + +def on_bg(img, color): + bg = Image.new('RGBA', img.size, color + (255,)) + bg.alpha_composite(img) + return bg + + +one_x = render(1) +one_x.save('hero-standing-pose.png') + +# 测试条: 掩尘底 / 裸土底 / 暗底 / 灰度(轮廓通道验证) +x8 = render(8) +tiles = [ + on_bg(x8, (201, 175, 131)), # 掩尘 #C9AF83 + on_bg(x8, (179, 154, 120)), # 裸土 #B39A78 + on_bg(x8, (46, 42, 38)), # 暗底对照 + ImageOps.grayscale(on_bg(x8, (201, 175, 131))).convert('RGBA'), +] +GAP = 24 +strip = Image.new('RGBA', + (len(tiles) * x8.width + (len(tiles) + 1) * GAP, x8.height + 2 * GAP), + (250, 244, 230, 255)) +for i, t in enumerate(tiles): + strip.alpha_composite(t, (GAP + i * (x8.width + GAP), GAP)) +strip.save('hero-standing-pose-preview.png') +print('done:', one_x.size, '->', strip.size) diff --git a/design/art/samples/hero-standing-pose-preview.png b/design/art/samples/hero-standing-pose-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..003a73433f311b442272756501d96ec56c30cf47 GIT binary patch literal 4904 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 eSUd_Cpa1|mBBHS5kt+iL0000