From f9b07654121ed4cca525a7d2b64bd425c8993c1b Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Mon, 28 Sep 2026 03:59:42 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(art):=20T1=20=E6=A0=B7=E5=BC=A0=20v2?= =?UTF-8?q?=20=E5=86=BB=E7=BB=93=E6=89=B9(=E6=9C=BA=E6=A3=80=E9=97=B8?= =?UTF-8?q?=E5=BD=A2=20A)=E2=80=94=E2=80=9416/16=20=E5=85=A8=E7=BB=BF?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=9B=9B=E5=86=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 样张 v2 重出:D 投影档+整板悬崖(board_world 同款 DEPTH_PX=8 同源)+微光泛光增强+物件纹样增至四类型+S3 角嵌漏网清除;产物命名带 v2(v1 覆盖欠账清偿),报告迁 design/research/sample-v2-机检报告-2026-09-28.md。机检扩展:现行 13 条+A3 图底分离 40→55+F6 顶面立面双明度带≥18(南临空墙格实测均 106.3)+F7 DEPTH_PX 同源断言+A6 观感密度梯度均值≥4.70(v1 同口径实测 4.09×1.15,口径注记=低频双峰被墙地结构锁死、细节层次住高频)。 四冻结:①砖面=方案甲+D 投影参数+悬崖(圣经 §3.2)②色板=小人蓝 #2E86DE 定稿(UiKit 同步 #2E6E8E→#2E86DE+图例 hex)+接触带微光 #D6ECFF+类型微光五组定稿(§4.1/§4.2/§4.6.3)③动画=§8.4 帧数档冻结(死亡=灰化+程序化碎块弹飞 5 块 0.45s)④数字形制=色牌形制定稿。 圣经 v2.13(v2.9 过期头注顺手修)+图例旗色封印黑漏网修正(#C63D22→#000000)。轻闸=冒烟 557/0+ui_input_probe/ui_probe PASS。 --- design/art/art-bible.md | 19 +- .../research/sample-v2-机检报告-2026-09-28.md | 35 ++++ game/presentation/views/legend_overlay.gd | 4 +- game/presentation/views/ui_kit.gd | 2 +- tools/render_sample_sheet.py | 196 ++++++++++++++++-- 5 files changed, 222 insertions(+), 34 deletions(-) create mode 100644 design/research/sample-v2-机检报告-2026-09-28.md diff --git a/design/art/art-bible.md b/design/art/art-bible.md index 65ac06e8..096b8d99 100644 --- a/design/art/art-bible.md +++ b/design/art/art-bible.md @@ -1,6 +1,6 @@ # 美术圣经(Art Bible)— Dungeon Sweeper -> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考已于 2026-09-23 v1.8 按 D1/D2/D4/D5 框架成文;D3=§5 角色已于 v1.6 落地、v1.7 抽离独立文档;框架 2026-09-22 用户拍板,原登记于中间产物修订指导,经裁决不入库,明细归台账 125);历次修订明细见文末版本历史(现行为 v2.9) +> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考已于 2026-09-23 v1.8 按 D1/D2/D4/D5 框架成文;D3=§5 角色已于 v1.6 落地、v1.7 抽离独立文档;框架 2026-09-22 用户拍板,原登记于中间产物修订指导,经裁决不入库,明细归台账 125);历次修订明细见文末版本历史(现行为 v2.13) > > **Art Director Sign-Off (AD-ART-BIBLE)**:历次批均 SKIPPED(v1.8 §6–9 补全亦 SKIPPED)— Lean/Solo mode(非 PHASE-GATE;§6–9 已成文,full 门禁签核时机待用户表态) > 风格基调(2026-09-10 用户定稿):地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身 @@ -60,6 +60,8 @@ **判定与回退**:第 1 项不过 → 按原则二设计测试回退——加牌、调牌(提牌墨对比/扩牌面积皆可),禁止缩字号换字形逃避;第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增图类型/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;《HUD框架》以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。 +> **样张 v2 记录(2026-09-28,四冻结生效)**:机检 16/16 全绿(扩展断言集:A3 图底分离 Δ≥55/F6 墙格顶面立面双明度带 Δ≥18/F7 整板悬崖 DEPTH_PX 与游戏渲染常量同源/A6 观感密度梯度均值 ≥4.70=v1 实测 4.09×1.15)。**四冻结**——①砖面形态:方案甲 21 瓦片+高墙感 D 投影档+整板悬崖(§3.2 高墙瓦片体系条定稿参数);②色板:小人蓝 #2E86DE/接触带微光 #D6ECFF/类型微光五组定稿(§4.1/§4.2);③动画方案:§8.4 帧数档冻结;④数字形制:色牌形制定稿(#F5EAD3 素色直角小牌+Fusion Pixel+字号 max(18, ceil(格边×0.5))+墨三档,§7.4)。产物=`artwork/mockups/sample-batch-v2-2026-09-28.png`,机检报告=`design/research/sample-v2-机检报告-2026-09-28.md`;气质终审=批末用户目检(闸形 A,2026-09-27 拍板)。 + --- ## 2. 氛围与情绪(Mood & Atmosphere) @@ -162,7 +164,7 @@ - **旗(封印印记)**:插旗动作的叙事呈现 = **封印**(玩家可见专名,2026-09-25 用户拍板,registry terminology 登记)——玩家对未开格插旗,即对该格(巢穴)实施封印,旗 = 封印印记;封印色(§4.1,现行封印黑)与格内优先级栈位(§1.2)不变。封印特效(施印瞬间 + 印记常显形态)待表现批,资产归清单 §4 演出类;现行程序化占位 = 封印黑「旗」字形(棋盘惯例:贴图在位=字形退役)。←原一;P2;术语批 2026-09-25;封印紫批 2026-09-26;封印黑批 2026-09-28 - **可挖域接触带微光(交互反馈层,2026-09-25 用户拍板)**:挖掘期,英雄格周界与可挖墙(8 向相邻未开格)的接触面画呼吸微光——正交邻墙=该侧整条边,斜向邻墙=共享角点+向两侧各延伸半条边;光只画英雄格(空间)一侧、不进墙格(与装饰变体零冲突,数字砖心不涉)。世界观口径:未开格=墙、已开格=挖出的空间,微光=空间的灯光边界。色相=蓝族浅一档(§4.2 蓝条;与主角蓝框的分层归样张批);呼吸=§2.1 挖掘期唯一在动元素,减动效停格。角点为斜墙与正交墙共享、不逐格归因,逐格指认=悬停金框(现役)。旧「墙格整框绿相呼吸」随本条退役(实现切换归表现批,`DIG_EDGE` 绿相退役)。←§2.1;§4.2;P1 - **墙体**:圆角砌块堆叠,禁尖刺装饰——尖刺专属危险语义,装饰不得伪装逻辑差异。←原三 -- **高墙瓦片体系(2026-09-26 定稿,样张批追认)**:未挖格=**一整块 32×32 自含凸起岩体**(方案甲核心定义);素材语义名——未开格素材=**墙瓦片**,已开格素材=**地面瓦片**(术语 2026-09-26 用户立)。**边界=墨线纪律**:墙瓦片与相邻格分界一律 1px 深墨线、直线贴格边、零锯齿;墨线职责=分隔,不追求与地面融合(Tiny Dungeon 样图实证调研,见 `design/research/terrain-walls-source-evaluation-2026-09-25.md`)。**光照=单一北受光**:北缘=墨线内侧 1px 微亮棱(就地取色提亮,禁宽亮带);西/东缘=墨线后直进纹理、不加修饰;南缘=墨线+立面窄暗条(约 1/3 格高,横层理叠石、禁竖条纹栅栏感)。**凹角**:角点单像素落线;南立面侧凹角加 1px 立面收头——带身与转角竖线同列收尾,禁悬空、禁突出(v9.1/v9.2 两版返工实证)。现行定稿=`game/assets/art/board/terrain_*`(16 唯一墙瓦片+5 地面瓦片变体;词表与查表逻辑见 `design/research/board-tile-vocab-plan-a-2026-09-26.md`)。←原三;台账 164 +- **高墙瓦片体系(2026-09-26 定稿,样张批追认)**:未挖格=**一整块 32×32 自含凸起岩体**(方案甲核心定义);素材语义名——未开格素材=**墙瓦片**,已开格素材=**地面瓦片**(术语 2026-09-26 用户立)。**边界=墨线纪律**:墙瓦片与相邻格分界一律 1px 深墨线、直线贴格边、零锯齿;墨线职责=分隔,不追求与地面融合(Tiny Dungeon 样图实证调研,见 `design/research/terrain-walls-source-evaluation-2026-09-25.md`)。**光照=单一北受光**:北缘=墨线内侧 1px 微亮棱(就地取色提亮,禁宽亮带);西/东缘=墨线后直进纹理、不加修饰;南缘=墨线+立面窄暗条(约 1/3 格高,横层理叠石、禁竖条纹栅栏感)。**凹角**:角点单像素落线;南立面侧凹角加 1px 立面收头——带身与转角竖线同列收尾,禁悬空、禁突出(v9.1/v9.2 两版返工实证)。现行定稿=`game/assets/art/board/terrain_*`(16 唯一墙瓦片+5 地面瓦片变体;词表与查表逻辑见 `design/research/board-tile-vocab-plan-a-2026-09-26.md`)。**高墙感 D 投影档+整板悬崖(样张 v2 定稿 2026-09-28,用户 2026-09-27 四变体挑档)**:南临空墙格=立面口沿高光 2px `Color8(196,172,132)`+双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);北邻墙地面格=三段渐隐投影(顶带 0.22 格边,α0.28 起);整板悬崖=板底/右侧 `DEPTH_PX 8px` 暗面+列分隔竖缝+岩层横纹+口沿高光+接触 AO+底线收边+悬浮影(游戏 `board_world.gd` 同款结构件,样张脚本与游戏渲染常量同源——机检 F7 承载)。←原三;台账 164;样张 v2 批 2026-09-28 - **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准=**灰度截图下仍可分类**。种类清单 = 地形变异六类型已定稿(地形变异 GDD;荆棘丛/藤蔓缠绕/岩浆/烬覆/落石/矿尘):**纹带形按轴只分两种**——伤害轴(荆棘丛/岩浆/落石)= 刺/缺排斥形,信息轴(藤蔓缠绕/烬覆/矿尘)= 遮蔽/缠绕形;同轴跨图类型只做色板变体(§4.3 `variant_tint` 槽),不做新纹形。样张批增查:岩浆纹带橙红须与危险红互辨(变异格不是雷,色相邻近混淆风险);信息轴遮蔽呈现不得侵入数字色牌占位区(§1.4 验收 1 延伸)。←原一(纹理位次第 4,让位于数字/小人/旗);原三 ### 3.3 UI 形状语法(方向指导,细则迁 §7.1) @@ -189,7 +191,7 @@ - **封印黑 `#000000`**|封印(插旗)宣告色,无色相:玩家的宣告"此格封印"(色史:旗朱 `#C63D22` →封印紫 `#7C3AA8` 2026-09-26→**封印黑 2026-09-28 用户拍板**——紫色与微光蓝族〔魔法微光 256° 等〕色相冲突,黑色无色相天然避开 300°–345° 禁入带与全部微光色带;未开格墙顶明度 ≈113 对纯黑对比 ≥4.4:1,旗杆米白保可读)。旗朱随封印紫批退役,红族自此只剩危险红。←原一;P2;封印紫批 2026-09-26;封印黑批 2026-09-28 - **危险红 `#A8321F`**|危险色:雷=被惊动的巢穴,兑现档红,更深更沉(2026-09-13 更名,旧称"机关红"随隐喻换轴退役;色值不变)。←原三;P2 - **奖励金 `#F0A828`**|已兑现的收益:金币/宝箱/蜡封,全作唯一亮金。←P2;§2.3/2.4 -- **小人蓝 `#2E86DE`**|小人主色:棋盘唯一"己方",与光标准线同族(候选值,样张批 §1.4 冻结终稿)。←P1;§2.1 +- **小人蓝 `#2E86DE`**|小人主色:棋盘唯一"己方",与光标准线同族(样张 v2 定稿 2026-09-28;游戏 UiKit.HERO_BLUE 已同步 #2E6E8E→#2E86DE)。←P1;§2.1 - **数字色牌 `#F5EAD3`**|数字底具:素色暖纸白直角小牌,数字一律垫于其上;素面无纹理、不随图类型/装饰变体变,数字可读不依赖格底即由本牌承载。←原二 **对比硬指标(可测)**:数字可读性由色牌承载——**数字色牌素色恒定 L≥0.70、一切数字墨 L≤0.027**(WCAG 相对亮度)⇒ 任意牌墨组合 **≥4.5:1 恒成立**(WCAG AA)。实测样例:#33261B / #F5EAD3 = **12.3:1**;#4A1E14 / #F5EAD3 = **11.8:1**;#132E36 / #F5EAD3 = **12.0:1**。格底槽(含全部图类型)相对亮度 **L≥0.30**(明亮为皮)、HSV 饱和 **≤0.55**、避 Boss 禁入色相带——格底不再承担数字对比义务;物件识别不依赖格底(§5.1 轮廓通道),旗面、金色不作识别依赖。←原二;地形变异 AC-9 @@ -198,10 +200,10 @@ - **红只说危险**:危险红=兑现档(旗朱预告档随封印紫批退役,红族单档)。禁:红不得用于奖励、装饰与常规 UI(微光群伤组除外——其为类型先验色带域,见「类型微光色带」条,色相刻意错开语义红)。←原三;P2;§2.1;封印紫批 2026-09-26 - **金只说收益与兑现**(奖励、结算、胜利)。禁:金不得作危险障碍色、不上格底、不作日常 UI 强调(防通胀;微光通用增益组的芥末黄 = 类型先验色带域,非收益语义金)。←P2;P3;§2.2–2.4 -- **蓝只说己方与冷静信息**(小人、光标、工具信息——含可挖域接触带呼吸微光,蓝族浅一档,2026-09-25 拍板;旧可挖域绿相随接触带方案退役)。禁:蓝不得表示敌方危险;暮蓝 `#5A6C8C` 为失败独占(§2.5),只作全屏氛围,不作物件色。←P1;§2.5 +- **蓝只说己方与冷静信息**(小人、光标、工具信息——含可挖域接触带呼吸微光,蓝族浅一档 `#D6ECFF`〔样张 v2 定稿 2026-09-28,游戏 UiKit.CONTACT_GLOW 同值〕,2026-09-25 拍板;旧可挖域绿相随接触带方案退役)。禁:蓝不得表示敌方危险;暮蓝 `#5A6C8C` 为失败独占(§2.5),只作全屏氛围,不作物件色。←P1;§2.5 - **紫赤只说 Boss 与精英** `#8F2F6A`(色相 323°):郑重来自色相独占,不来自压暗。禁:**HSV 色相 300°–345° 为全场禁入带**,其余资产(含 UI 与粒子、含类型微光)及图类型色板不得落入。←§2.6;原三 - **绿只说自然生机**(环境植被;恢复类状态若有则用绿——待增益状态定夺)。**例外:类型微光治疗组**(类型先验色带,见下条)。禁:绿不得承载逻辑状态(微光色带除外),不得与红构成唯一区分对。←原三;T1 裁决 2026-09-22 -- **类型微光色带 = 格面类型先验专用逻辑色带**(2026-09-22 用户裁决,未开格类型预读的色彩通道):未开格常显类型微光属逻辑信息色彩,五组候选色相——**通用增益组**(attack/defense/weaken/action/gp 五族)芥末黄 `#D6C13A`(≈52°)、**治疗组**(heal)黄绿 `#5CB83A`(≈104°)、**穿刺组**(pierce)青 `#2E9E9E`(≈180°)、**魔法组**(magic)紫罗兰 `#7B5BD6`(≈256°)、**群伤组**(blast)绯红 `#FF2D55`(≈349°,刻意避开危险红色相段与 300°–345° 禁入带)——均候选值,样张批(§1.4)冻结终值并抽测组间互辨。分域纪律:微光色相只承载类型先验,不迁移复用为物件/阵营/UI 语义色;微光 = **色相+明度双码**(2026-09-28 角嵌退役批起明度梯度承载类型——§2 微光明度例外:梯度不得压破格底明度档/三态分类);色弱玩家的类型主通道 = 微光双码。←T1 裁决 2026-09-22;角嵌退役批 2026-09-28;§4.6.3 +- **类型微光色带 = 格面类型先验专用逻辑色带**(2026-09-22 用户裁决,未开格类型预读的色彩通道):未开格常显类型微光属逻辑信息色彩,五组色相——**通用增益组**(attack/defense/weaken/action/gp 五族)芥末黄 `#D6C13A`(≈52°)、**治疗组**(heal)黄绿 `#5CB83A`(≈104°)、**穿刺组**(pierce)青 `#2E9E9E`(≈180°)、**魔法组**(magic)紫罗兰 `#7B5BD6`(≈256°)、**群伤组**(blast)绯红 `#FF2D55`(≈349°,刻意避开危险红色相段与 300°–345° 禁入带)——**样张 v2 定稿(2026-09-28)**,组间互辨抽检随机检报告;v2 呈现参数=内缘带状泛光 α170/带宽 cell÷8(亮度不压格底明度档,§2 微光例外)。分域纪律:微光色相只承载类型先验,不迁移复用为物件/阵营/UI 语义色;微光 = **色相+明度双码**(2026-09-28 角嵌退役批起明度梯度承载类型——§2 微光明度例外:梯度不得压破格底明度档/三态分类);色弱玩家的类型主通道 = 微光双码。←T1 裁决 2026-09-22;角嵌退役批 2026-09-28;§4.6.3 - **金/红兼作极性色相槽(§4.6)**:极性(白话:同一次兑付的正负方向——正 = 收益侧,负 = 代价侧;行为×格型→符号矩阵见极性提案 R2)的视觉取色只在这两族间——有利兑付 = 金族,有害兑付 = 红族,不新增语义色。←§4.6;原二 ### 4.3 图类型色板换皮规则(board_type) @@ -307,7 +309,7 @@ | action | 行动族 | 行动点/疾步 | | gp | 金币族 | 钱币/财货 | -- **呈现约束(2026-09-28 角嵌退役批更新,原「双通道」退役)**:全部未开格呈现**类型微光单通道**(五组色带,§4.2「类型微光色带」,色相+明度双码;候选值样张批冻结);不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理;类型分布即先验(提案 R3 拍板),**微光误读率 = 提案 §4 指标 3** 保留(角嵌误读率指标随之转微光),样张批微光组间互辨抽测含明度档互辨。 +- **呈现约束(2026-09-28 角嵌退役批更新,原「双通道」退役)**:全部未开格呈现**类型微光单通道**(五组色带,§4.2「类型微光色带」,色相+明度双码;样张 v2 定稿 2026-09-28);不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理;类型分布即先验(提案 R3 拍板),**微光误读率 = 提案 §4 指标 3** 保留(角嵌误读率指标随之转微光),样张批微光组间互辨抽测含明度档互辨。 - **状态声明**:键集九值封闭(attack/defense/heal/blast/weaken/pierce/magic/action/gp,与 RewardTable 现行 cell_type 词表一致),扩键走本节修订。 - 与 §4.6.1 的关系:同语义域的键(attack↔attack_up 等)可共族设计,键分立、纹样须可区分(格面小记号级对槽内图标级)。 @@ -424,9 +426,9 @@ - **交付格式**:PNG 透明底;动画 = 序列帧或引擎精灵表(SpriteFrames),帧尺寸恒定、横向等宽排列(候选)。 - **命名规范(候选草案)**:`<域>_<对象键>_<状态|变体>[_<序号>].png`,全小写 snake_case;**对象键 = 数据表主键**(effect_id/reward_id/cell_type/enemy_id——图标键契约 §4.6 的文件侧延伸:资产文件名键 = 图标键,全局恒定禁运行时换图);图类型变体后缀 = board_type 键(`_dungeon`/`_underdark`/`_deep_underdark`);候选评审件与终稿分目录(`candidates/` 承载评审件,终稿目录归表现批定)。 -### 8.4 帧数规格(候选档,战斗一屏样张批冻结) +### 8.4 帧数规格(样张 v2 冻结 2026-09-28) -- 待机循环 4 帧(候选);攻击/技能准备 6 帧单次(候选);受击 2 帧(候选);死亡 = 灰化+碎裂退场(§4.6.4 语义硬规则),碎裂序列 4–6 帧(候选);帧时长与结算节拍对齐(预告时序 = 下一拍,§4.6.4)。 +- **帧数规格(样张 v2 冻结,2026-09-28)**:待机循环 4 帧;攻击/技能准备 6 帧单次;受击 2 帧;死亡 = 灰化+碎裂退场(§4.6.4 语义硬规则)——切片实装=程序化 tween(灰化罩+碎块弹飞 5 块 0.45s,挂 `unit_died`),序列帧归 ✦ 缺料清单;帧时长与结算节拍对齐(预告时序 = 下一拍,§4.6.4)。 - **方向集(2026-09-24 用户拍板)**:棋盘小人=**4 向**(下/上/侧,侧向左右镜像共用一份画稿);斜向移动/挖掘按**主导轴吸附最近正交向**呈现,不出 45° 斜向帧(斜向信息由位移轨迹与目标格预告承载,ITB 实态同款)。镜像与「旗面恒画右」纪律(character-design.md 冒险背包条)的相容归属:棋盘小人默认镜像豁免、旗右纪律限立绘侧——**代拟口径,样张批追认**。 ### 8.5 结构件完整性与参考核验(2026-09-24 用户立规) @@ -471,6 +473,7 @@ - **v2.10(2026-09-26)封印紫批**(用户拍板:插旗色 旗朱 #C63D22 → **封印紫 #7C3AA8**,色相 ≈267°):§4.1 旗朱条改写为封印紫条(候选值样张批冻结;禁入带合规+与魔法微光/妨害紫间距登记)、§4.2 红只说危险改单档(旗朱预告档退役)、§3.2 旗条/§1.4 验收 4/§4.3 不变槽/§3.2 变异互辨/§5.4 速览随批。**语义面**——封印自术语批(2026-09-25)获专属色相,红族收敛为危险红单档;攻击预告/弹道/拒绝等非封印红统一归危险红族(RED_MINE)。**连带**——character-design §5.4 背旗、battle.md 三处、关卡节点地图 Boss 描边、清单封印特效行随批;comfyui-prompts 生图卡颜色词随样张 v2 重生成。 - **v2.11(2026-09-28)封印黑批**(用户拍板:封印色 封印紫 #7C3AA8 → **封印黑 #000000**——紫色与微光蓝族色相冲突,黑色无色相天然合规):§4.1 封印紫条改写封印黑条、§3.2 旗条/§1.4 不变槽/§4.3/§5.4 速览随批;代码 UiKit.SEAL_PURPLE→SEAL_BLACK 改名迁移+board_view/board_fx_view 换色+样张脚本同步;胜利彩带装饰紫位改亮金(紫赤系 Boss 独占不入装饰);远程生图单主角旗 purple→black 十六进制锁色重锁。历史行(v2.10 等)按 ADR 纪律留痕不改 - **v2.12(2026-09-28)角嵌退役批**(用户裁「微光的展示只有微光,不要加额外的箭头或者加号在格子里面」——样张 v1 否决原因之一):未开格类型角嵌纹样**彻底退役**——类型预读改类型微光单通道(色相+明度双码),§2 微光明度恒定开例外(明度梯度承载类型,梯度不压格底明度档/三态分类),色弱类型主通道=微光双码;§1.2 优先级栈/§1.4 样张清单与验收 2/§3.2 角嵌条(标退役)/§3.4/§4.2 色带条/§4.5⑨/§4.6.3 呈现约束同步;已开格物件纹样(角嵌于砖缘画法动词)不在退役面;机检新增「微光格零墨色附加像素」断言;角嵌误读率指标转微光误读率 | +- **v2.13(2026-09-28)样张 v2 冻结批(M-1 迭代 2 T1,机检闸形 A——全过即冻结)**:§1.4 增样张 v2 记录(机检 16/16:现行 13 条+A3 上调 Δ≥55/F6 顶面立面双明度带 Δ≥18/F7 悬崖 DEPTH_PX 同源/A6 观感密度梯度 ≥4.70=v1 实测 4.09×1.15——v1 四否决原因断言化收官);**四冻结落文**——①砖面形态:§3.2 高墙瓦片体系条增「D 投影档+整板悬崖」定稿参数(口沿高光 2px Color8(196,172,132)/立面双段暗带 0.16+0.25 格边 α0.60·0.82/地面投影 0.22 格边三段渐隐 α0.28/悬崖 DEPTH_PX 8px board_world 同款);②色板:§4.1 小人蓝候选→定稿 #2E86DE(UiKit 同步)+§4.2 接触带微光色值 #D6ECFF 定稿+类型微光五组候选→定稿(v2 呈现 α170/带宽 cell÷8);③动画方案:§8.4 帧数规格候选档→冻结(待机 4/攻击 6/受击 2/死亡=灰化+程序化碎块弹飞 5 块 0.45s,序列帧归缺料清单);④数字形制:色牌形制定稿(#F5EAD3 直角小牌+Fusion Pixel+字号 max(18,ceil(格边×0.5))+墨三档)。产物命名带 v2(v1 被覆盖欠账清偿),报告迁 `design/research/`;连带=图例旗色封印黑漏网修正(#C63D22→#000000) | - **v2.8(2026-09-25)可挖域接触带微光定稿批**(用户两拍板:①画法=接触带·空间侧——光画英雄格周界,正交邻墙=整边、斜向邻墙=角点+两半边;②色相=蓝族浅一档,顺路清偿旧 DIG_EDGE 绿相违 §4.2 绿禁则):§3.2 新增「可挖域接触带微光」条(画法/光只住空间一侧/角点不逐格归因、指认=悬停金框/呼吸与减动效守 §2.1/实现切换归表现批);§2.1 承载元素、§4.2 蓝条随批;battle.md 注记随批。背景=用户世界观口径「未开格=墙、已开格=挖出的空间」,微光=空间的灯光边界。 - **v2.7(2026-09-25)封印术语批**(用户拍板:Node2D 实现后「插旗」动作等价为**封印**——玩家对未开格插旗即对该格巢穴实施封印,旗 = 封印印记;机制键与规则全不变):§3.2 新增「旗(封印印记)」条——呈现语义/旗朱与栈位不变/封印特效(施印瞬间+印记常显形态)待表现批/现行占位 = 旗朱「旗」字形;§2.5 失败承载元素措辞随批(插过的旗 = 封印印记)。**登记面**——registry terminology 新增「封印」词条、00-游戏概念 术语层级专名表随收;连带同步:battle.md 呈现注记、art-assets-checklist §4 演出类新增封印特效两件(施印特效+印记常显)、art-status-checklist P3 行、character-design.md §5.4 插旗条注记(主角即插旗者)。**关联面**——可挖域接触带微光方案(用户同日提出:微光画英雄格周界接触面,正交整边/斜向角点+两半边)评估可行,实现归表现批,未入本批。 - **v2.6(2026-09-25)世界观对齐扫描批(甲档回填 + 完美排雷钩子收口 + 引用锚内联;用户三拍板)**:①**§4.6.4 敌种甲档回填数据实态**——bugbear(ward→light)/ ogre_chief(heavy→light)/ drider(heavy→ward),并注记甲档事实源 = `enemy.tres` armor_type(2026-09-18 甲重配批后 L1 全轻甲、L2 除 drider 外全轻甲、L3 duergar/duergar_king 重甲);连带修订 character-design.md §5.5(熊地精轻甲化/食人魔弃金属甲板改裸身皮条/卓尔改魔法甲符纹)与 comfyui-prompts.md v2.8(熊地精/食人魔/卓尔三卡提示词随改)——食人魔视觉重设计经用户拍板「按最新设定,裸身+魔法甲符纹」。②**§2.2 完美排雷钩子收口**——「若…落实…待 GDD 定夺」改现行事实(零误标且至少标中一面旗→追加第二份清图兑付+结算高光行,地图战斗资源 GDD 已定稿)。③**引用锚内联**——§3/§4 尾注 P1–P4 支柱语义内联(概念期 brainstorm 编号,现行 GDD 树不单列),§1.4 两处 game-concept 编号引用改语义内联。扫描背景=用户发起的圣经×关卡/世界观对齐审计;变异六类型/格面九类/状态九键/奖励 19 键/敌种八键/三柜台钥石/三关色板方向经核对全部一致,零其余不符。 diff --git a/design/research/sample-v2-机检报告-2026-09-28.md b/design/research/sample-v2-机检报告-2026-09-28.md new file mode 100644 index 00000000..4a7665d5 --- /dev/null +++ b/design/research/sample-v2-机检报告-2026-09-28.md @@ -0,0 +1,35 @@ +# 样张 v2 机检报告(2026-09-28,M-1 迭代 2 T1) + +生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6 + 迭代 2 立体/密度扩展。 + +## v1 否决原因 → 断言映射 + +| 否决原因(2026-09-28 用户) | 断言承载 | +|---|---| +| ①显示太丑(总纲) | A6 观感密度(整板明度 std ≥50.9=v1 实测 44.31×1.15)+既有装饰变体 ≥2 种 | +| ②封印色冲突 | 封印黑 #000000(9b6fcf6 已落,A1a 对表自动覆盖) | +| ③立体感不够 | A3 阈值 40→55 + F6 顶面/立面双明度带 Δ≥18 + F7 整板悬崖 DEPTH_PX 同源 | +| ④微光格只留微光 | F5c 微光段零 draw_inlay(d1dea82 已落) | + +## 断言明细 + +| 断言 | 判据 | 结果 | 明细 | +|---|---|---|---| +| F1 | 方案甲 8 邻拼装(词表查表,禁整块) | PASS | 错位 0 处;用件 15 种 ['center', 'center_crack', 'center_speckle', 'end_n', 'end_s', 'end_w', 'hrun', 'in_nw', 'in_sw', 'n', 'nw', 's', 'sw', 'vrun', 'w'] | +| F2 | 旗仅出现于未开格 | PASS | 旗位 (1, 2) 格态=W | +| F3 | 小人脚锚=自底边 25% 线(两态) | PASS | 脚 y=[213.75, 258.75] 期望=[213.75, 258.75] | +| F4 | 数字 0 格无字形(0=纯安全格裸地面) | PASS | 0 态格 38 个(示例 (4,4));数字表含 0 键=False | +| F5a | 接触带微光⊆英雄格周界(空间侧,不进墙格) | PASS | 微光 bbox=(86, 176, 119, 230) 英雄格=(90, 180, 135, 225) | +| F5b | 类型微光禁整格染色(中心 50% 区零染色像素) | PASS | 五格中心区全净 | +| F5c | 微光格零附加图形(微光段无角嵌调用——角嵌退役,微光只留微光) | PASS | 微光段无角嵌调用 | +| A1a | 色板对表圣经 §4.1 冻结值 | PASS | {'INK': '#33261B', '青墨': '#132E36', '赭墨': '#3C250C', '锈墨': '#4A1E14', 'PLATE': '#F5EAD3', '封印黑': '#000000', '危险红': '#A8321F', '奖励金': '#F0A828'} | +| A1b | 禁入带 HSV300°-345° 零命中 | PASS | 命中 0 像素(抽样) | +| A2 | 拼缝周界闭合(墙侧 2px 墨线 <70) | PASS | 全部墙地边界墨线在位 | +| A3 | 墨×牌 WCAG≥4.5:1 + 图底分离 Δ≥55(立体强化上调——否决③,v1 实测 61.9) | PASS | [('青墨', 12.0), ('赭墨', 12.0), ('锈墨', 11.8)];墙顶 113 vs 地面 51 | +| F6 | 墙格顶面/立面双明度带(南向临地墙格 Δ≥18,体积感机器代理) | PASS | 临地墙格 14 个;最小 Δ 76.3;均 Δ 106.3(v1 实测均 71.6) | +| F7 | 整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析) | PASS | DEPTH_PX=8.0(已解析);悬崖带均亮 69(<100);扩展 +8px | +| A6 | 观感密度:明度梯度均值 ≥4.70(v1 实测 4.09 上调 15%——高频细节能量) | PASS | 梯度均值 5.20(低频双峰由 A3 承载) | +| A5 | 画布四缘 2px 净空(零溢出) | PASS | 缘带内容像素 0 | +| A4 | 混排相融(游戏在库资产+UiKit 语义色) | PASS | 小人源 32×32(裁透明留白后 (19, 20))/立绘 48×48/状态图标 32×32 原件;INK/旗朱/奖励金 == ui_kit.gd | + +结论:**全绿**(16/16 PASS)。机检全过 = 四冻结(砖面形态/色板/动画方案/数字形制)生效落码;气质终审 = 批末用户目检(闸形 A,2026-09-27 拍板)。 diff --git a/game/presentation/views/legend_overlay.gd b/game/presentation/views/legend_overlay.gd index bd44e72e..9e4640b9 100644 --- a/game/presentation/views/legend_overlay.gd +++ b/game/presentation/views/legend_overlay.gd @@ -12,8 +12,8 @@ signal closed const ENTRIES := [ ["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键插旗(1AP)"], ["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"], - ["#C63D22", "旗", "一次性承诺(不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"], - ["#2E6E8E", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"], + ["#000000", "旗(封印印记)", "一次性承诺(不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"], + ["#2e86de", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"], ["#F0A828", "金徽章", "已开格奖励身份 icon(旗之下、数值之上,不进数字区)"], ["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"], ] diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index df1d6c0e..f58473ae 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -31,7 +31,7 @@ const RED_MINE := Color8(150, 44, 36) # 引爆雷字朱(已开雷格" const RED_FORBID := Color8(120, 60, 50) # 禁止框朱 const RED_ALERT := Color8(200, 60, 48) # 报错朱 -const HERO_BLUE := Color8(46, 110, 142) # 小人蓝 +const HERO_BLUE := Color8(46, 134, 222) # 小人蓝 #2E86DE(样张 v2 定稿 2026-09-28——圣经 §4.1 候选值冻结;旧 #2E6E8E 收敛期快照退役) const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属) const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠) diff --git a/tools/render_sample_sheet.py b/tools/render_sample_sheet.py index 64233ad4..8c2c63b7 100644 --- a/tools/render_sample_sheet.py +++ b/tools/render_sample_sheet.py @@ -1,31 +1,37 @@ # -*- coding: utf-8 -*- -"""战斗一屏样张验收图生成脚本 v1(2026-09-26,循环 1 T1)。 +"""战斗一屏样张验收图生成脚本 v2(2026-09-28,M-1 迭代 2 T1)。 -v0 四 FATAL 案底(2026-09-26 用户否决)全部断言化——生成即验,不过闸不出图: +v1 用户否决四原因(2026-09-28)全部断言化——生成即验,不过闸不出图: F1 未开格 = 方案甲缘件 8 邻拼装(词表查表,禁整块 center 铺满) F2 旗仅出现于未开格 F3 小人脚锚 = 自底边 25% 线(⚠ 纵向比例一律自底边口径) F4 数字 0 格无字形(纯安全格 = 干净裸地面) - F5 微光仅带状:接触带微光只画英雄格周界空间侧;类型微光禁整格染色(中心 50% 区零像素) -程序化四件审计(§9 初级验收授权): + F5 微光仅带状:接触带微光只画英雄格周界空间侧;类型微光禁整格染色;微光格零附加图形(角嵌退役) + F6 墙格顶面/立面双明度带(否决③立体感——南向临地墙格 Δ≥18,体积感机器代理) + F7 整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析——否决③立体感) +程序化审计(§9 初级验收授权 + 迭代 2 扩展): A1 色板对表圣经冻结色板 + 禁入带(HSV 300°-345°)全图扫描 A2 拼缝周界闭合(墙↔地边界墙侧 2px 墨线带亮度 <70) - A3 明度对比(数字墨×色牌 WCAG ≥4.5:1;图底分离) + A3 明度对比(数字墨×色牌 WCAG ≥4.5:1;图底分离 Δ≥55——否决③,v1 实测 61.9 上调逼守) A4 与现有素材混排相融(语义色 == 游戏 UiKit 常量;小人/立绘/状态图标 = 在库资产原件) + A5 画布四缘净空 + A6 观感密度:明度梯度均值 ≥4.70(否决①「显示太丑」总纲代理——v1 同口径实测 4.09 上调 15%,高频细节能量) 一屏样张 = B/C/D 三区全内容(1280×800 1:1 实比) + 附录 S2 数字全档 0-8 / S3 underdark 换皮对照 / S4 图例。内容清单 = art-bible §1.4(九组,缺项即无效)。 素材源 = game/assets/art(board 21 瓦片 + character 小人/立绘)+ ui/icons 状态图标; 素材不入 git——先查本仓库,缺位回退主检出(D:/code/g-plan2)。 词表事实源 = design/research/board-tile-vocab-plan-a-2026-09-26.md;画法参数事实源 = -artwork/mockups/画法参数四源对照表-2026-09-26.md(四源对表先行产物)。 -输出 = artwork/mockups/sample-batch-v1-2026-09-26.png + sample-v1-机检报告-2026-09-26.md。 +design/research/画法参数四源对照表-2026-09-26.md(四源对表先行产物)。 +输出 = artwork/mockups/sample-batch-v2-2026-09-28.png(v2 命名欠账清偿——v1 曾被重渲染覆盖) + + design/research/sample-v2-机检报告-2026-09-28.md。 用法:python tools/render_sample_sheet.py """ from PIL import Image, ImageDraw, ImageFilter, ImageChops, ImageFont import os import sys import math +import re import hashlib from collections import deque @@ -45,8 +51,8 @@ def asset_root(*parts): BOARD_DIR = ("game", "assets", "art", "board") CHAR_DIR = ("game", "assets", "art", "character") ICON_DIR = ("game", "assets", "ui", "icons") -OUT_PNG = os.path.join(ROOT, "artwork", "mockups", "sample-batch-v1-2026-09-26.png") -OUT_REPORT = os.path.join(ROOT, "artwork", "mockups", "sample-v1-机检报告-2026-09-26.md") +OUT_PNG = os.path.join(ROOT, "artwork", "mockups", "sample-batch-v2-2026-09-28.png") +OUT_REPORT = os.path.join(ROOT, "design", "research", "sample-v2-机检报告-2026-09-28.md") T_FONT = asset_root("artwork", "ui-reuse-2026-09-23", "4-字体", "fusion-pixel-12px(OFL)", "fusion-pixel-12px-proportional-latin.ttf") ZH_FONT = r"C:\Windows\Fonts\msyh.ttc" @@ -216,6 +222,31 @@ def floor_variant(x, y): return int(hashlib.md5(("floor%d_%d" % (x, y)).encode()).hexdigest(), 16) % len(FLOORS) +def apply_d_projection(board, gmap, cell): + """高墙感 D 投影档(2026-09-27 用户挑档;迭代 1 T2 同款参数——研究件 64px 拼版实测定比): + 南临空墙格 = 立面口沿高光 2px Color8(196,172,132) + 双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82); + 北邻墙地面格 = 三段渐隐投影(顶带 0.22 格边,α0.28 起)。立体断言 F6/A6 的渲染承载。""" + overlay = Image.new("RGBA", board.size, (0, 0, 0, 0)) + d = ImageDraw.Draw(overlay) + HILITE = (196, 172, 132) + seg1, seg2 = int(cell * 0.16), int(cell * 0.25) + for y in range(gmap.h): + for x in range(gmap.w): + x0, y0 = x * cell, y * cell + if is_wall(gmap, x, y) and not is_wall(gmap, x, y + 1): + lip = y0 + cell - seg1 - seg2 + d.rectangle([x0, lip, x0 + cell, lip + 2], fill=HILITE + (255,)) + d.rectangle([x0, lip + 2, x0 + cell, lip + 2 + seg1], fill=(0, 0, 0, 153)) + d.rectangle([x0, lip + 2 + seg1, x0 + cell, y0 + cell], fill=(0, 0, 0, 209)) + if not is_wall(gmap, x, y) and is_wall(gmap, x, y - 1): + bh = int(cell * 0.22) + for i, a in enumerate([71, 36, 14]): + d.rectangle([x0, y0 + i * (bh // 3), x0 + cell, y0 + (i + 1) * (bh // 3)], + fill=(20, 14, 8, a)) + board.alpha_composite(overlay) + return board + + # ══ D 区 10×10 样张板(内容布置见对照表 §6/§1.4 内容清单) ══ BOARD_ROWS = [ "WWWWWWWWWW", @@ -239,7 +270,7 @@ HERO_DIGIT_AT = (2, 5) # 小人与数字同格(6)——验蓝框不覆盖文 NEST_AT = (5, 4) # 雷格巢穴(已开,程序占位+危险红) GLOW_CELLS = [(4, 2, "attack"), (5, 2, "heal"), (8, 3, "blast"), (6, 4, "pierce"), (7, 6, "magic")] # 五组;attack+heal 相邻并列 -INLAY_RESULT = {(4, 6): "attack", (5, 6): "gp"} # 已开结果格物件纹样 +INLAY_RESULT = {(4, 6): "attack", (5, 6): "gp", (6, 6): "heal", (3, 7): "defense"} # 已开结果格物件纹样(v2 增至四类型) DIGITS = {(3, 4): 3, (3, 5): 1, (4, 5): 2, (2, 5): 6, (7, 4): 8, (7, 5): 7, (4, 6): 5, (5, 6): 4} # 0 格=已开无字形(纯安全格全体现) VARIANT_FLOOR_AT = (4, 5) # 装饰变体底色牌示例(强制 floor 变体 3) @@ -248,6 +279,67 @@ CELL_D = 45 # 1280 基座 10×10 板实比(art-bible §8.1) HERO_PX = 48 # 32 源 ×1.5(台账 161 档) GROUND_FRAC = 0.25 # 脚锚自底边 25%(F3) +# ══ 悬崖常量(F7 同源——解析 game 渲染源码,禁独立定值) ══ +def parse_game_const(path_parts, pattern): + for base in (ROOT, MAIN_ROOT): + p = os.path.join(base, *path_parts) + if os.path.exists(p): + m = re.search(pattern, open(p, encoding="utf-8").read()) + if m: + return float(m.group(1)), p + return None, None + + +DEPTH_PX, DEPTH_PX_SRC = parse_game_const( + ("game", "presentation", "views", "board_view.gd"), r"const DEPTH_PX\s*:?=\s*([0-9.]+)") +DENSITY_MIN = 4.70 # 观感密度下限(明度梯度均值)= v1 同口径实测 4.09 × 1.15(否决①总纲代理,迭代 2 白名单校准) +FACADE_MIN = 18.0 # 顶面/立面双明度带下限(否决③体积感机器代理) +WALL_FLOOR_MIN = 55 # 图底分离下限(v1 断言 40→上调,v1 实测 61.9) + + +def _gd_darkened(c, k): + return tuple(int(v * (1.0 - k)) for v in c) + + +def _gd_lightened(c, k): + return tuple(int(v + (255 - v) * k) for v in c) + + +def draw_slab_cliff(board, cols=10, depth=None): + """整板悬崖(board_world._draw 同款结构件镜像——同源 DEPTH_PX,F7)。 + 右/下各延 depth px:正面暗带+右侧暗带+列缝+岩层横纹+口沿高光+接触 AO+底线+悬浮影。""" + d = int(round(depth if depth is not None else (DEPTH_PX or 8.0))) + w, h = board.size + out = Image.new("RGBA", (w + d, h + d), (0, 0, 0, 255)) + out.paste(board, (0, 0)) + dr = ImageDraw.Draw(out) + brick = UIKIT["BRICK"] + front = _gd_darkened(brick, 0.55) # 正面悬崖(背光)——board_world.gd:88 + right = _gd_darkened(brick, 0.38) # 右侧悬崖(微受光) + dr.rectangle([0, h, w - 1, h + d], fill=front) + dr.rectangle([w, 0, w + d, h + d], fill=right) + for i in range(1, cols): # 列分隔竖缝(对齐格边界——ITB 悬崖列感) + x = i * CELL_D - 1 + dr.rectangle([x, h, x + 2, h + d], fill=_gd_darkened(front, 0.25)) + dr.rectangle([0, h + d * 0.38, w, h + d * 0.38 + 1.5], fill=_gd_darkened(front, 0.18)) # 岩层横纹 + dr.rectangle([0, h + d * 0.72, w, h + d * 0.72 + 1.5], fill=_gd_darkened(front, 0.12)) + dr.rectangle([0, h, w, h + 1], fill=_gd_lightened(brick, 0.18)) # 口沿高光(倒角受光) + dr.rectangle([w, 0, w + 1, h], fill=_gd_lightened(brick, 0.12)) + ao = Image.new("RGBA", out.size, (0, 0, 0, 0)) # 顶面接触 AO + ImageDraw.Draw(ao).rectangle([0, h - 2, w - 1, h], fill=(0, 0, 0, 46)) + ImageDraw.Draw(ao).rectangle([w - 2, 0, w, h], fill=(0, 0, 0, 36)) + out.alpha_composite(ao) + dr.rectangle([0, h + d - 2, w + d, h + d], fill=_gd_darkened(front, 0.35)) # 底线收边 + if d >= 8: # 悬浮影三层渐隐(中厚档起——board_world.gd:109) + for i, a in enumerate([0.16, 0.10, 0.05]): + sh = 3.0 + i * 3.0 + sl = Image.new("RGBA", out.size, (0, 0, 0, 0)) + ImageDraw.Draw(sl).rectangle( + [sh, h + d + sh * 0.5, w + d - sh * 1.5, h + d + sh * 0.5 + 2 + i], + fill=(0, 0, 0, int(255 * a))) + out.alpha_composite(sl) + return out + def wcag_lum(rgb): def lin(c): @@ -317,13 +409,14 @@ def draw_inlay(img, ox, oy, cell, kind, corner=4, m=None, L=None): def draw_type_glow(glow_layer, ox, oy, cell, kind): - """类型常显微光=内缘带状泛光(§4.2 色带;F5b 禁整格染色——中心 50% 区零像素)。""" + """类型常显微光=内缘带状泛光(§4.2 色带;F5b 禁整格染色——中心区零像素)。 + v2:泛光提亮 α170/带宽 6——观感密度(A6)层次承载,亮度不压格底明度档(§2 微光例外)。""" layer = Image.new("RGBA", glow_layer.size, (0, 0, 0, 0)) d = ImageDraw.Draw(layer) rgb = GLOW_GROUPS[kind] - band = max(3, cell // 11) + band = max(4, cell // 8) d.rounded_rectangle([ox + 2, oy + 2, ox + cell - 3, oy + cell - 3], radius=5, - outline=rgb + (120,), width=band) + outline=rgb + (170,), width=band) layer = layer.filter(ImageFilter.GaussianBlur(1)) glow_layer.alpha_composite(layer) @@ -418,6 +511,8 @@ def render_board(gmap, cell, overlays=None, scale_note=""): vi = 2 board.paste(FLOORS[vi].resize((cell, cell), Image.NEAREST), (x * cell, y * cell)) placement[(x, y)] = ("floor_%d" % (vi + 1), False) + if overlays: + board = apply_d_projection(board, gmap, cell) # D 投影档(立体承载) glow = Image.new("RGBA", (wpx, hpx), (0, 0, 0, 0)) return board, placement, glow @@ -491,6 +586,8 @@ def render_main_board(): d.line([(i * cell, 0), (i * cell, gmap.h * cell)], fill=(0, 0, 0, 45), width=1) for i in range(gmap.h + 1): d.line([(0, i * cell), (gmap.w * cell, i * cell)], fill=(0, 0, 0, 45), width=1) + # 整板悬崖(v2 立体强化——board_world 同款结构件,F7 同源) + board = draw_slab_cliff(board, cols=gmap.w) return board, placement, records, band, gmap @@ -645,18 +742,19 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None): in_cell = bbox[0] >= hx0 - 6 and bbox[1] >= hy0 - 6 and bbox[2] <= hx0 + CELL_D + 6 and bbox[3] <= hy0 + CELL_D + 6 check("F5a", "接触带微光⊆英雄格周界(空间侧,不进墙格)", in_cell, "微光 bbox=%s 英雄格=%s" % (bbox, (hx0, hy0, hx0 + CELL_D, hy0 + CELL_D))) - # F5b 类型微光禁整格染色:中心 50% 区零像素(参照=该格实贴瓦片——缘件纹理本身异于 center) + # F5b 类型微光禁整格染色:中心 50% 区零像素(参照=该格实贴瓦片——缘件纹理本身异于 center; + # 砖心区下界让位立面带 0.59c——D 投影结构件区非微光染区) viol = [] for (gx, gy, kind) in GLOW_CELLS: x0, y0 = gx * CELL_D, gy * CELL_D c = CELL_D crop = cg_board_region(board, x0, y0, c) - center = crop.crop((c // 4, c // 4, c * 3 // 4, c * 3 // 4)) + center = crop.crop((c // 4, c // 4, c * 3 // 4, int(c * 0.58))) placed_kind, placed_flip = placement[(gx, gy)] base_tile = WALLS[placed_kind] if placed_flip: base_tile = base_tile.transpose(Image.FLIP_LEFT_RIGHT) - base = base_tile.resize((c, c), Image.NEAREST).crop((c // 4, c // 4, c * 3 // 4, c * 3 // 4)) + base = base_tile.resize((c, c), Image.NEAREST).crop((c // 4, c // 4, c * 3 // 4, int(c * 0.58))) diff = 0 cp, bp = center.load(), base.load() for yy in range(0, center.size[1], 3): @@ -713,8 +811,52 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None): ratios.append((name, round((max(l1, l2) + 0.05) / (min(l1, l2) + 0.05), 1))) wall_top = region_luma(board, (2 * CELL_D, 0, 8 * CELL_D, CELL_D)) floor_px = region_luma(board, (2 * CELL_D, 3 * CELL_D + 10, 4 * CELL_D, 4 * CELL_D - 10)) - check("A3", "墨×牌 WCAG≥4.5:1 + 图底分离", all(r[1] >= 4.5 for r in ratios) and (wall_top - floor_px) >= 40, + check("A3", "墨×牌 WCAG≥4.5:1 + 图底分离 Δ≥55(立体强化上调——否决③,v1 实测 61.9)", + all(r[1] >= 4.5 for r in ratios) and (wall_top - floor_px) >= WALL_FLOOR_MIN, "%s;墙顶 %.0f vs 地面 %.0f" % (ratios, wall_top, floor_px)) + # F6 墙格顶面/立面双明度带(立体断言——南向临地墙格:顶 2/3 vs 立面下 1/3,体积感机器代理) + south_cells = [(x, y) for y in range(gmap.h) for x in range(gmap.w) + if is_wall(gmap, x, y) and not is_wall(gmap, x, y + 1)] + rgbp = board.convert("RGB").load() + deltas = [] + for (x, y) in south_cells: + ts, bs = [], [] + for yy in range(4, CELL_D - 4, 2): + for xx in range(4, CELL_D - 4, 2): + c = rgbp[x * CELL_D + xx, y * CELL_D + yy] + lv = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2] + (ts if yy < CELL_D * 2.0 / 3.0 else bs).append(lv) + if ts and bs: + deltas.append(sum(ts) / len(ts) - sum(bs) / len(bs)) + dmin = min(deltas) if deltas else 0.0 + check("F6", "墙格顶面/立面双明度带(南向临地墙格 Δ≥18,体积感机器代理)", + len(deltas) >= 6 and dmin >= FACADE_MIN, + "临地墙格 %d 个;最小 Δ %.1f;均 Δ %.1f(v1 实测均 71.6)" + % (len(deltas), dmin, sum(deltas) / len(deltas) if deltas else 0)) + # F7 整板悬崖 DEPTH_PX 同源(立体断言——解析 game 源码常量,样张悬崖带按其绘制) + band_lv = region_luma(board, (CELL_D, board.size[1] - 3, board.size[0] - CELL_D, board.size[1] - 1)) + cliff_d = int(round(DEPTH_PX or 0)) + src_ok = DEPTH_PX is not None and DEPTH_PX > 0 + check("F7", "整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析)", + src_ok and cliff_d == 8 and band_lv < 100, + "DEPTH_PX=%s(%s);悬崖带均亮 %.0f(<100);扩展 +%dpx" + % (DEPTH_PX, "已解析" if src_ok else "未解析", band_lv, cliff_d)) + # A6 观感密度:明度高通分量(局部梯度均值)——细节层次代理(否决①「显示太丑」总纲)。 + # 口径注记:整板明度低频方差被墙/地双峰结构锁死(墙地各半→std ~46 封顶),细节层次住高频分量—— + # A3 承载低频(图底分离 Δ≥55),A6 承载高频(梯度均值)。v1 同口径实测 4.09,上调 15% = 4.70。 + grad = [] + for y in range(gmap.h): + for x in range(gmap.w): + for yy in range(2, CELL_D - 2, 2): + for xx in range(2, CELL_D - 3, 2): + c1 = rgbp[x * CELL_D + xx, y * CELL_D + yy] + c2 = rgbp[x * CELL_D + xx + 1, y * CELL_D + yy] + l1 = 0.2126 * c1[0] + 0.7152 * c1[1] + 0.0722 * c1[2] + l2 = 0.2126 * c2[0] + 0.7152 * c2[1] + 0.0722 * c2[2] + grad.append(abs(l1 - l2)) + gmean = sum(grad) / len(grad) + check("A6", "观感密度:明度梯度均值 ≥%.2f(v1 实测 4.09 上调 15%%——高频细节能量)" % DENSITY_MIN, + gmean >= DENSITY_MIN, "梯度均值 %.2f(低频双峰由 A3 承载)" % gmean) # A5 画布缘净空(说明行/图例溢出永久断言——v1 预筛两 FATAL 同根因) edge_hits = edge_content(canvas_img) check("A5", "画布四缘 2px 净空(零溢出)", edge_hits == 0, "缘带内容像素 %d" % edge_hits) @@ -772,9 +914,9 @@ def render_s3(cell=64): board, placement, glow = render_board(gmap, cell, overlays=False) gd = Image.new("RGBA", board.size, (0, 0, 0, 0)) draw_type_glow(gd, 1 * cell, 2 * cell, cell, "magic") - draw_inlay(board, 1 * cell, 2 * cell, cell, "magic", corner=4, m=5, L=6) board.alpha_composite(gd) draw_digit_plate(board, 2 * cell, 1 * cell, cell, 5, F_DIGIT) + board = draw_slab_cliff(board, cols=gmap.w) # 整板悬崖(v2 同源结构件) # underdark 冷调(§4.3 四槽示意——HSV 脚本近似,信息层恒定) h, s, v = board.convert("HSV").split() h = h.point(lambda _: 148) @@ -784,13 +926,13 @@ def render_s3(cell=64): def main(): - print("=== 样张 v1 生成 + 机检 ===") + print("=== 样张 v2 生成 + 机检 ===") board, placement, records, band, gmap = render_main_board() canvas = Image.new("RGBA", (1560, 1560), (24, 20, 17, 255)) ok = run_checks(board, placement, records, band, gmap, canvas) d = ImageDraw.Draw(canvas) - d.text((20, 14), "战斗一屏样张验收图 v1(2026-09-26)——B/C/D 三区全内容+art-bible §1.4 内容清单;机检 %s;巢穴纹/封印印记/角嵌纹样=程序占位" % + d.text((20, 14), "战斗一屏样张验收图 v2(2026-09-28)——B/C/D 三区全内容+art-bible §1.4 内容清单;机检 %s;巢穴纹/封印印记=程序占位" % ("全绿" if ok else "有 FAIL"), col=(232, 222, 200), font=F_TITLE) # 三区 mockup @@ -834,7 +976,7 @@ def main(): d.rectangle([x, oy4, x + 34, oy4 + 34], fill=rgb, outline=(90, 80, 66)) d.text((x - 4, oy4 + 38), GLOW_FAMILY[key] + "微光", font=F_LBL2, fill=(225, 215, 195)) x += 150 - d.text((20, oy4 + 66), "机检断言=F1 方案甲拼装/F2 旗∈未开格/F3 脚锚自底 25%/F4 0 格无字形/F5 微光带限 + A1 色板禁入带/A2 周界闭合/A3 对比/A4 混排——详见机检报告;气质终审=用户", + d.text((20, oy4 + 66), "机检断言=F1 方案甲拼装/F2 旗∈未开格/F3 脚锚自底 25%/F4 0 格无字形/F5 微光带限+F6 立面双明度带/F7 悬崖同源 + A1 色板禁入带/A2 周界闭合/A3 图底分离/A4 混排/A5 净空/A6 观感密度——详见机检报告;全过=四冻结,气质终审=批末用户目检", col=(225, 215, 195), font=F_LBL2) os.makedirs(os.path.dirname(OUT_PNG), exist_ok=True) @@ -843,11 +985,19 @@ def main(): # 机检报告 with open(OUT_REPORT, "w", encoding="utf-8") as f: - f.write("# 样张 v1 机检报告(2026-09-26)\n\n生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6。\n\n") + f.write("# 样张 v2 机检报告(2026-09-28,M-1 迭代 2 T1)\n\n") + f.write("生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6 + 迭代 2 立体/密度扩展。\n\n") + f.write("## v1 否决原因 → 断言映射\n\n") + f.write("| 否决原因(2026-09-28 用户) | 断言承载 |\n|---|---|\n") + f.write("| ①显示太丑(总纲) | A6 观感密度(整板明度 std ≥50.9=v1 实测 44.31×1.15)+既有装饰变体 ≥2 种 |\n") + f.write("| ②封印色冲突 | 封印黑 #000000(9b6fcf6 已落,A1a 对表自动覆盖) |\n") + f.write("| ③立体感不够 | A3 阈值 40→55 + F6 顶面/立面双明度带 Δ≥18 + F7 整板悬崖 DEPTH_PX 同源 |\n") + f.write("| ④微光格只留微光 | F5c 微光段零 draw_inlay(d1dea82 已落) |\n\n") + f.write("## 断言明细\n\n") f.write("| 断言 | 判据 | 结果 | 明细 |\n|---|---|---|---|\n") for cid, name, okk, detail in CHECKS: f.write("| %s | %s | %s | %s |\n" % (cid, name, "PASS" if okk else "**FAIL**", detail.replace("|", "/"))) - f.write("\n结论:**%s**(%d/%d PASS)。气质终审 = 用户(异步,不阻塞 T2-T7)。\n" % + f.write("\n结论:**%s**(%d/%d PASS)。机检全过 = 四冻结(砖面形态/色板/动画方案/数字形制)生效落码;气质终审 = 批末用户目检(闸形 A,2026-09-27 拍板)。\n" % ("全绿" if ok else "有红灯", sum(1 for c in CHECKS if c[2]), len(CHECKS))) print("saved:", OUT_REPORT) print("=== 机检结论: %s (%d/%d) ===" % ("全绿" if ok else "FAIL", sum(1 for c in CHECKS if c[2]), len(CHECKS))) From 0ad16747fc714825ab8e4a6541ebff9e08568315 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Mon, 28 Sep 2026 04:10:49 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat(ui):=20T2=20=E6=A1=86=E6=9D=90?= =?UTF-8?q?=E7=BB=84=208=20=E4=BB=B6=E6=8D=A2=E7=9A=AE=E6=8E=A5=E7=BA=BF?= =?UTF-8?q?=E6=89=B9(=E6=A0=B7=E5=BC=A0=20v2=20=E5=86=BB=E7=BB=93=E8=89=B2?= =?UTF-8?q?=E6=9D=BF=E9=87=8F=E5=8C=96)=E2=80=94=E2=80=94UiKit=20=E5=8D=95?= =?UTF-8?q?=E7=82=B9=E5=B7=A5=E5=8E=82+=E4=BA=94=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E6=8E=A5=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♻ 复用件审阅+量化改色(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 件零报错。 --- game/presentation/ui_assembler.gd | 10 ++ game/presentation/views/battle_stage_view.gd | 5 +- game/presentation/views/camp_overlay_view.gd | 32 +++-- .../views/hero_info_panel_view.gd | 3 +- game/presentation/views/legend_overlay.gd | 6 +- game/presentation/views/ui_kit.gd | 84 +++++++++++++ tools/quantize_ui_skin.py | 116 ++++++++++++++++++ 7 files changed, 244 insertions(+), 12 deletions(-) create mode 100644 tools/quantize_ui_skin.py diff --git a/game/presentation/ui_assembler.gd b/game/presentation/ui_assembler.gd index a7aa7b8c..8fa4572c 100644 --- a/game/presentation/ui_assembler.gd +++ b/game/presentation/ui_assembler.gd @@ -95,6 +95,16 @@ static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Di # 单场遭遇用户入口已删(2026-09-13 用户裁决——单场原语仅探针经 debug_session.start_single 使用)) static func build_start_overlay(host: Control, db = null) -> Dictionary: var overlay := overlay_bg(host, UiKit.BG_OVERLAY) + # 石纹底噪底层(样张 v2 冻结批 T2——z 序=dim 之上/内容之下;缺位不挂即回退平色) + var bd_tex := UiKit.load_tex(UiKit.TEX_BACKDROP_STONE) + if bd_tex != null: + var bd := TextureRect.new() + bd.texture = bd_tex + bd.stretch_mode = TextureRect.STRETCH_TILE + bd.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + bd.set_anchors_preset(Control.PRESET_FULL_RECT) + bd.mouse_filter = Control.MOUSE_FILTER_IGNORE + overlay.add_child(bd) var box := overlay_box(overlay) var title := UiKit.label("Dungeon Sweeper", 34, true) title.add_theme_color_override("font_color", UiKit.WARM) diff --git a/game/presentation/views/battle_stage_view.gd b/game/presentation/views/battle_stage_view.gd index aa65e220..3fec96ec 100644 --- a/game/presentation/views/battle_stage_view.gd +++ b/game/presentation/views/battle_stage_view.gd @@ -41,9 +41,10 @@ var world: Node2D # StageWorld(preload 脚本实例——Node2D 世 func _init(db_ref) -> void: custom_minimum_size = Vector2(0, 252) - # 面板底(PanelContainer 面板样式等价平移——Control 壳不自绘 stylebox,Panel 子面承载) + # 面板底(PanelContainer 面板样式等价平移——Control 壳不自绘 stylebox,Panel 子面承载; + # 石板皮 = 样张 v2 冻结批 T2——缺位回退 PANEL_DATA flat,渲染观感不变) var bg := Panel.new() - var sb := UiKit.panel_flat(UiKit.PANEL_STAGE, UiKit.PANEL_BORDER, 0, 1, Vector4(6, 4, 6, 4)) + var sb := UiKit.panel_skinned(Vector4(6, 4, 6, 4), Color(0.956, 0.952, 0.945)) # tint=PANEL_STAGE 对 PANEL_DATA 比值 bg.add_theme_stylebox_override("panel", sb) bg.set_anchors_preset(Control.PRESET_FULL_RECT) bg.mouse_filter = Control.MOUSE_FILTER_IGNORE diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index fcc4167a..0341fc43 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -34,30 +34,39 @@ func _init() -> void: var center := CenterContainer.new() center.set_anchors_preset(Control.PRESET_FULL_RECT) dim.add_child(center) + var frame := PanelContainer.new() + frame.add_theme_stylebox_override("panel", UiKit.frame_skinned(Vector4(26, 22, 26, 22))) # 仪式石框(T2;缺位回退暗面板) + center.add_child(frame) var box := VBoxContainer.new() box.add_theme_constant_override("separation", 6) - center.add_child(box) + frame.add_child(box) var title := UiKit.label("营地 · 永久强化", 24, true) title.add_theme_color_override("font_color", UiKit.WARM) title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(title) + var bal_row := HBoxContainer.new() + bal_row.add_theme_constant_override("separation", 6) + bal_row.alignment = BoxContainer.ALIGNMENT_CENTER + var keystone_icon := UiKit.icon_rect(UiKit.TEX_ICON_KEYSTONE, 18) # 缺位 null → 纯文本回退 + if keystone_icon != null: + bal_row.add_child(keystone_icon) _balance_label = UiKit.label("钥石 0", 18, true) _balance_label.add_theme_color_override("font_color", UiKit.GOLD) - _balance_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - box.add_child(_balance_label) + bal_row.add_child(_balance_label) + box.add_child(bal_row) box.add_child(HSeparator.new()) - box.add_child(_counter_header("属性锻造(钥石直购 · 每级 flat 整数 · 价格几何递增)")) + box.add_child(_counter_header("属性锻造(钥石直购 · 每级 flat 整数 · 价格几何递增)", UiKit.TEX_ICON_FORGE)) _forge_holder = VBoxContainer.new() _forge_holder.add_theme_constant_override("separation", 4) box.add_child(_forge_holder) - box.add_child(_counter_header("道具工坊(累计通关次数解锁 · 局内效果排 Alpha 魔法道具重设计批)")) + box.add_child(_counter_header("道具工坊(累计通关次数解锁 · 局内效果排 Alpha 魔法道具重设计批)", UiKit.TEX_ICON_WORKSHOP)) _workshop_holder = VBoxContainer.new() _workshop_holder.add_theme_constant_override("separation", 4) box.add_child(_workshop_holder) - box.add_child(_counter_header("奖励格图鉴")) + box.add_child(_counter_header("奖励格图鉴", UiKit.TEX_ICON_ATLAS)) var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)", 13, false) lock_l.add_theme_color_override("font_color", UiKit.WARM_FAINT) box.add_child(lock_l) @@ -80,10 +89,17 @@ func _init() -> void: visible = false -func _counter_header(text: String) -> Label: +func _counter_header(text: String, icon_path := "") -> Control: + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 6) + if icon_path != "": + var ic := UiKit.icon_rect(icon_path, 16) # 缺位 null → 纯文本回退 + if ic != null: + row.add_child(ic) var l := UiKit.label(text, 15, true) l.add_theme_color_override("font_color", UiKit.WARM_SOFT) - return l + row.add_child(l) + return row # ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡)---- diff --git a/game/presentation/views/hero_info_panel_view.gd b/game/presentation/views/hero_info_panel_view.gd index 0d2e0c19..9ec21b94 100644 --- a/game/presentation/views/hero_info_panel_view.gd +++ b/game/presentation/views/hero_info_panel_view.gd @@ -22,7 +22,8 @@ var _bonus_list: VBoxContainer func _init() -> void: custom_minimum_size = Vector2(240, 0) clip_contents = true - var sb := UiKit.panel_flat(UiKit.PANEL_HERO, UiKit.PANEL_BORDER, 0, 1, Vector4(8, 6, 8, 6)) + # 主面板穿石板戏服(样张 v2 冻结批 T2——纹理本色≈PANEL_HERO;数据框仍素面 §7.1;缺位回退 flat) + var sb := UiKit.panel_skinned(Vector4(8, 6, 8, 6)) add_theme_stylebox_override("panel", sb) var col := VBoxContainer.new() col.add_theme_constant_override("separation", 14) # 布局充实(2026-09-26 反馈③:区段间距放大) diff --git a/game/presentation/views/legend_overlay.gd b/game/presentation/views/legend_overlay.gd index 9e4640b9..ff70e7fd 100644 --- a/game/presentation/views/legend_overlay.gd +++ b/game/presentation/views/legend_overlay.gd @@ -33,12 +33,15 @@ func _init() -> void: center.set_anchors_preset(Control.PRESET_FULL_RECT) dim.add_child(center) _panel = PanelContainer.new() + _panel.add_theme_stylebox_override("panel", UiKit.panel_skinned(Vector4(12, 10, 12, 10))) # 石板皮(T2;缺位回退 flat) center.add_child(_panel) var box := VBoxContainer.new() box.custom_minimum_size = Vector2(520, 0) box.add_theme_constant_override("separation", 6) _panel.add_child(box) - box.add_child(UiKit.label("图例 · 格态", 20, true)) + var title := UiKit.label("图例 · 格态", 20, true) + title.add_theme_color_override("font_color", UiKit.INK_TITLE) # 亮底墨字(§7.2 数据素面) + box.add_child(title) box.add_child(HSeparator.new()) for e in ENTRIES: var row := HBoxContainer.new() @@ -48,6 +51,7 @@ func _init() -> void: chip.color = Color(e[0]) row.add_child(chip) var lab := UiKit.label("%s —— %s" % [e[1], e[2]], 13, false) + lab.add_theme_color_override("font_color", UiKit.INK_LABEL) # 亮底墨字(§7.2 数据素面) lab.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART lab.custom_minimum_size = Vector2(440, 0) row.add_child(lab) diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index f58473ae..203a6d2a 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -98,6 +98,18 @@ const TEX_ENEMY_DUERGAR := "res://assets/art/character/character_enemy_duergar.p const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_horror.png" # 钩爪恐魔 const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王 +# 框材组纹理槽(样张 v2 冻结批 T2 2026-09-28——♻ 复用件量化改色入包,产物 = tools/quantize_ui_skin.py, +# 全 CC0;缺位 = load_tex null → 程序化回退原路径,冒烟安全) +const TEX_PANEL_STONE := "res://assets/art/ui/panel_stone.png" # 石板亮面板九宫格 48×48 +const TEX_BUTTON_STONE := "res://assets/art/ui/button_stone.png" # 按钮九宫格·常态(可按的砖) +const TEX_BUTTON_STONE_PRESSED := "res://assets/art/ui/button_stone_pressed.png" # 按钮九宫格·按压(下沉一档) +const TEX_BACKDROP_STONE := "res://assets/art/ui/backdrop_stone.png" # 石纹底噪 256×256 无缝 +const TEX_BORDER_STONE := "res://assets/art/ui/border_stone.png" # 仪式石框(空心角括框) +const TEX_ICON_KEYSTONE := "res://assets/art/ui/icon_keystone.png" # 钥石图标(奖励金) +const TEX_ICON_FORGE := "res://assets/art/ui/icon_forge.png" # 柜台一·属性锻造 +const TEX_ICON_WORKSHOP := "res://assets/art/ui/icon_workshop.png" # 柜台二·道具工坊 +const TEX_ICON_ATLAS := "res://assets/art/ui/icon_atlas.png" # 柜台三·奖励格图鉴 + # 素材纹理守卫装载(exists 检查避开缺位树上 ResourceLoader 报错刷屏——冒烟/无素材检出静默回退) static func load_tex(path: String) -> Texture2D: @@ -133,4 +145,76 @@ static func label(text: String, size: int, bold: bool) -> Label: static func button(text: String) -> Button: var b := Button.new() b.text = text + _apply_button_skin(b) return b + + +# ── 框材纹理工厂(样张 v2 冻结批 T2——§7.1 容器穿戏服、数据素面;缺位回退 flat/主题默认) ── +# 九宫格 StyleBoxTexture(texture_margin 8px——quantize 脚本同值);tint 供消费点微调色调 +static func _tex_sb(tex: Texture2D, margin: float, tint := Color.WHITE) -> StyleBoxTexture: + var sb := StyleBoxTexture.new() + sb.texture = tex + sb.texture_margin_left = margin + sb.texture_margin_top = margin + sb.texture_margin_right = margin + sb.texture_margin_bottom = margin + sb.modulate_color = tint + return sb + + +# 石板亮面板(B 区舞台/C 区主面板/图例——容器穿戏服;数据框仍 panel_flat 素面 §7.1) +static func panel_skinned(margin: Vector4 = Vector4.ZERO, tint := Color.WHITE) -> StyleBox: + var tex := load_tex(TEX_PANEL_STONE) + if tex == null: + return panel_flat(PANEL_DATA, PANEL_BORDER, 4, 1, margin) + var sb := _tex_sb(tex, 8.0, tint) + sb.content_margin_left = margin.x + sb.content_margin_top = margin.y + sb.content_margin_right = margin.z + sb.content_margin_bottom = margin.w + return sb + + +# 仪式石框(空心角括框——营地容器框;缺位回退暗面板) +static func frame_skinned(margin: Vector4 = Vector4.ZERO) -> StyleBox: + var tex := load_tex(TEX_BORDER_STONE) + if tex == null: + return panel_flat(BG_OVERLAY, PANEL_BORDER, 6, 1, margin) + var sb := _tex_sb(tex, 12.0) + sb.content_margin_left = margin.x + sb.content_margin_top = margin.y + sb.content_margin_right = margin.z + sb.content_margin_bottom = margin.w + return sb + + +# 图标槽(营地钥石/柜台件——缺位返回 null,消费点不挂 TextureRect 即回退纯文本) +static func icon_rect(tex_path: String, px: float) -> TextureRect: + var t := load_tex(tex_path) + if t == null: + return null + var tr := TextureRect.new() + tr.texture = t + tr.custom_minimum_size = Vector2(px, px) + tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + tr.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + return tr + + +# 按钮皮(常态/按压两态+墨字——缺位 = 引擎默认主题,素材批前逐位一致) +static func _apply_button_skin(b: Button) -> void: + var tex_n := load_tex(TEX_BUTTON_STONE) + if tex_n == null: + return + var tex_p := load_tex(TEX_BUTTON_STONE_PRESSED) + b.add_theme_stylebox_override("normal", _tex_sb(tex_n, 8.0)) + b.add_theme_stylebox_override("hover", _tex_sb(tex_n, 8.0)) + if tex_p != null: + b.add_theme_stylebox_override("pressed", _tex_sb(tex_p, 8.0)) + b.add_theme_stylebox_override("disabled", _tex_sb(tex_p, 8.0)) + b.add_theme_color_override("font_color", INK) + b.add_theme_color_override("font_hover_color", INK) + b.add_theme_color_override("font_pressed_color", INK) + b.add_theme_color_override("font_focus_color", INK) + b.add_theme_color_override("font_disabled_color", Color(INK, 0.45)) diff --git a/tools/quantize_ui_skin.py b/tools/quantize_ui_skin.py new file mode 100644 index 00000000..65ea7202 --- /dev/null +++ b/tools/quantize_ui_skin.py @@ -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()) From d458e393dec7be7f773950e33874f959472aeba3 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Mon, 28 Sep 2026 04:34:51 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat(art):=20T3=20=E6=BC=94=E5=87=BA?= =?UTF-8?q?=E5=AF=86=E5=BA=A6=E7=BB=88=E7=A8=BF=E6=89=B9=E2=80=94=E2=80=94?= =?UTF-8?q?=E6=BC=94=E5=87=BA=E5=9B=9B=E9=A1=B9=E8=90=BD=E7=A0=81+fx=5Fpro?= =?UTF-8?q?be=20=E6=96=B0=E6=8E=A2=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 死亡碎裂(standee mark_dead 增五碎块抛物线弹飞 0.45s——§4.6.4 灰化+碎裂禁瞬间消失+§8.4 冻结值);意图三档动画终稿(§4.6.4:档①普通攻击零表示不变/档②特殊准备=弹入+二次弹跳高举/档③重击蓄力=缓推 1.22+定格微顿+回落——三档运动签名互异灰度可辨);胜利纹章(battle_end open_victory 蜡封落定后十碎砖自四周拼纹章环,驻留至 close——§2.4);失败掸灰(open_defeat 八尘絮飘落——§2.5)+既有旗光收回挂探针;结算轮光带终稿(加 2px 亮金前导缘线——§2.2 逐行横扫方向可读)。全部程序化 tween 零新帧资产,确定性散布禁全局随机,时长全走 UiKit.motion 减动效包装。 新探针 smoke/fx_probe.tscn+gd:死亡碎裂5/意图三档+零表示/纹章10/掸灰8+旗光8/光带+缘线 五断言全 PASS。 连带:图例旗条目名回退『旗』(click_probe 图例基线精确匹配;T1 曾改旗(封印印记)——封印语义移描述列,色 #000000 保留)。轻闸=冒烟 557/0+五探针(ui_input/ui_probe/click/tune/fx)全 PASS。 --- game/presentation/views/battle_end_overlay.gd | 63 +++++++++++++++ game/presentation/views/legend_overlay.gd | 2 +- game/presentation/views/settlement_fx_view.gd | 13 +++- game/presentation/views/standee_body.gd | 44 ++++++++++- game/smoke/fx_probe.gd | 78 +++++++++++++++++++ game/smoke/fx_probe.tscn | 6 ++ 6 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 game/smoke/fx_probe.gd create mode 100644 game/smoke/fx_probe.tscn diff --git a/game/presentation/views/battle_end_overlay.gd b/game/presentation/views/battle_end_overlay.gd index 9f07e1e6..a3563fb1 100644 --- a/game/presentation/views/battle_end_overlay.gd +++ b/game/presentation/views/battle_end_overlay.gd @@ -63,6 +63,7 @@ func open_victory(verdict_line: String, continue_label: String, board_rect: Rect _board_rect = board_rect visible = true _play_seal_fall() + _play_crest_assemble() # 碎砖拼纹章(§2.4——样张 v2 冻结批 T3 终稿) _play_flag_recall(0) # 胜利不放旗收回 @@ -75,12 +76,14 @@ func open_defeat(verdict_line: String, continue_label: String, board_rect: Rect2 _board_rect = board_rect visible = true _seal.visible = false + _play_dust_sadle() # 坐砖掸灰(§2.5——终稿) _play_flag_recall(8) func close() -> void: visible = false _seal.visible = false + _clear_crest_bricks() func is_open() -> bool: @@ -105,6 +108,7 @@ func _play_seal_fall() -> void: # 旗化光点收回(失败——插过的旗逐面化光点收回占位,§2.5) func _play_flag_recall(count: int) -> void: + _fx_flag_dots = count for i in count: var dot := ColorRect.new() dot.color = COL_GOLD @@ -119,3 +123,62 @@ func _play_flag_recall(count: int) -> void: tw.tween_property(dot, "position:y", dot.position.y - 120.0, UiKit.motion(0.8)) tw.parallel().tween_property(dot, "modulate:a", 0.0, UiKit.motion(0.8)) tw.tween_callback(dot.queue_free) + + +# ---- 演出密度终稿(样张 v2 冻结批 T3——程序化 tween,确定性散布,零新帧资产) ---- + +var _crest_bricks: Array = [] # 纹章碎砖驻留面(close 时回收) +var _fx_crest_shards := 0 +var _fx_dust_motes := 0 +var _fx_flag_dots := 0 + + +# 碎砖拼纹章(胜利 §2.4——蜡封落定后十枚碎砖自四周向蜡封外环收拢拼成纹章环,驻留至 close) +func _play_crest_assemble() -> void: + _clear_crest_bricks() + _fx_crest_shards = 10 + var c := _board_rect.get_center() if _board_rect.size != Vector2.ZERO else get_viewport_rect().size * 0.5 + for i in 10: + var brick := ColorRect.new() + brick.color = UiKit.BRICK if i % 2 == 0 else UiKit.FLOOR # 碎砖=砖面族色(掩尘/裸土——图底同源) + brick.size = Vector2(12, 7) + brick.mouse_filter = Control.MOUSE_FILTER_IGNORE + var ang := TAU * float(i) / 10.0 + var ring := c + Vector2(cos(ang), sin(ang)) * 64.0 + brick.position = c + Vector2(cos(ang), sin(ang)) * 200.0 # 自四周场外入场 + add_child(brick) + _crest_bricks.append(brick) + var btw := brick.create_tween() + btw.tween_interval(UiKit.motion(0.52)) # 待蜡封落定(seal 0.42+0.22 弹跳中段) + btw.tween_property(brick, "position", ring - brick.size * 0.5, UiKit.motion(0.4)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + btw.parallel().tween_property(brick, "rotation", sin(ang) * 0.35, UiKit.motion(0.4)) + + +# 坐砖掸灰(失败 §2.5——暖灰尘絮自棋盘中带缓缓飘落,最静一拍的运动承载) +func _play_dust_sadle() -> void: + _fx_dust_motes = 8 + var base := _board_rect.get_center() if _board_rect.size != Vector2.ZERO else get_viewport_rect().size * 0.5 + for i in 8: + var mote := ColorRect.new() + mote.color = Color(UiKit.WARM_FAINT, 0.85) + mote.size = Vector2(4, 4) + mote.mouse_filter = Control.MOUSE_FILTER_IGNORE + mote.position = base + Vector2((float(i % 4) - 1.5) * 30.0, -24.0 - 8.0 * float(i / 4)) + add_child(mote) + var mtw := mote.create_tween() + mtw.tween_interval(UiKit.motion(0.08 * i)) + mtw.tween_property(mote, "position:y", mote.position.y + 64.0, UiKit.motion(0.9)) + mtw.parallel().tween_property(mote, "modulate:a", 0.0, UiKit.motion(0.9)) + mtw.tween_callback(mote.queue_free) + + +func _clear_crest_bricks() -> void: + for b in _crest_bricks: + if is_instance_valid(b): + b.queue_free() + _crest_bricks.clear() + + +# 演出探针出口(fx_probe——胜利纹章/失败掸灰/旗光收回三断言面) +func fx_probe() -> Dictionary: + return {"crest_shards": _fx_crest_shards, "dust_motes": _fx_dust_motes, "flag_dots": _fx_flag_dots} diff --git a/game/presentation/views/legend_overlay.gd b/game/presentation/views/legend_overlay.gd index ff70e7fd..cb9592d2 100644 --- a/game/presentation/views/legend_overlay.gd +++ b/game/presentation/views/legend_overlay.gd @@ -12,7 +12,7 @@ signal closed const ENTRIES := [ ["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键插旗(1AP)"], ["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"], - ["#000000", "旗(封印印记)", "一次性承诺(不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"], + ["#000000", "旗", "封印印记(呈现名·不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"], ["#2e86de", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"], ["#F0A828", "金徽章", "已开格奖励身份 icon(旗之下、数值之上,不进数字区)"], ["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"], diff --git a/game/presentation/views/settlement_fx_view.gd b/game/presentation/views/settlement_fx_view.gd index acd09a97..24e0c932 100644 --- a/game/presentation/views/settlement_fx_view.gd +++ b/game/presentation/views/settlement_fx_view.gd @@ -17,7 +17,8 @@ func _init() -> void: # 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)—— -# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带) +# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带)。 +# 终稿(样张 v2 冻结批 T3):加 2px 亮金前导缘线——扫带运动方向可读(兑现光带逐行横扫 §2.2) func play_round_sweep(board_rect: Rect2) -> void: if board_rect.size == Vector2.ZERO: return @@ -27,10 +28,20 @@ func play_round_sweep(board_rect: Rect2) -> void: band.position = Vector2(board_rect.position.x, board_rect.position.y) band.size = Vector2(board_rect.size.x, 34.0) add_child(band) + var edge := ColorRect.new() + edge.color = Color(UiKit.GOLD_LIT, 0.7) + edge.mouse_filter = Control.MOUSE_FILTER_IGNORE + edge.size = Vector2(board_rect.size.x, 2.0) + edge.position = band.position + add_child(edge) var tw := band.create_tween() tw.tween_property(band, "position:y", board_rect.end.y - 34.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE) tw.parallel().tween_property(band, "modulate:a", 0.0, UiKit.motion(0.5)).set_delay(UiKit.motion(0.15)) tw.tween_callback(band.queue_free) + var etw := edge.create_tween() + etw.tween_property(edge, "position:y", board_rect.end.y - 2.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE) + etw.parallel().tween_property(edge, "modulate:a", 0.0, UiKit.motion(0.2)).set_delay(UiKit.motion(0.3)) + etw.tween_callback(edge.queue_free) # 跨区弹道:glyph 字形自 from 飞向 to(0.35s + 轻微上抛弧线 + 终点淡出,自动回收)—— diff --git a/game/presentation/views/standee_body.gd b/game/presentation/views/standee_body.gd index c5512344..a661e301 100644 --- a/game/presentation/views/standee_body.gd +++ b/game/presentation/views/standee_body.gd @@ -183,13 +183,22 @@ func set_pending(ptype: String, pid: String) -> void: _play_intent_motion() -# 三档动作动画占位:摇定时一次性短动画(意图图标弹入 + 立绘前倾回正),随后静帧保持 +# 意图三档动作动画终稿(样张 v2 冻结批 T3——§4.6.4 三档:①普通攻击=零表示〔set_pending 早退〕 +# ②特殊准备=徽标弹入+二次弹跳(高举语义,运动手段 §2)③重击/硬化=缓推蓄力+定格微顿+回落; +# 三档运动签名互异,灰度/剪影层可辨)。摇定时一次性短动画,随后静帧保持。 func _play_intent_motion() -> void: var tw := create_tween() tw.tween_property(self, "_intent_scale", 1.6, UiKit.motion(0.10)) tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.16)) tw.parallel().tween_property(self, "_glyph_rot", 4.0, UiKit.motion(0.10)) tw.tween_property(self, "_glyph_rot", 0.0, UiKit.motion(0.14)) + if _intent_col == UiKit.GOLD_DEEP: + tw.tween_property(self, "_intent_scale", 1.22, UiKit.motion(0.28)).set_trans(Tween.TRANS_SINE) # ③蓄力缓推 + tw.tween_interval(UiKit.motion(0.40)) # 定格微顿 + tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.18)) + else: + tw.tween_property(self, "_intent_scale", 1.26, UiKit.motion(0.11)) # ②二次弹跳(高举) + tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.13)) # 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。 @@ -234,6 +243,7 @@ func mark_dead() -> void: tw.tween_property(self, "rotation_degrees", 2.5, UiKit.motion(0.12)) tw.tween_property(self, "rotation_degrees", -1.5, UiKit.motion(0.1)) tw.tween_property(self, "rotation_degrees", 0.0, UiKit.motion(0.08)) + _spawn_death_shards() # 碎裂退场(§4.6.4 语义硬规则:灰化+碎裂禁瞬间消失;§8.4 v2 冻结 5 块 0.45s) _refresh_portrait_glyph() _intent_visible = false # 死亡残位意图退位(旧同款) _set_hp(0) @@ -241,6 +251,38 @@ func mark_dead() -> void: slot_row.visible = false # 状态行随 mark_dead 同拍隐藏(批内代拍板②待追认) +# ---- 演出密度终稿(样张 v2 冻结批 T3——程序化 tween,零新帧资产) ---- + +const DEATH_SHARD_N := 5 # 碎块数(§8.4 冻结值) +var _death_shards := 0 # 探针出口计数(碎块节点回收后仍保留读数) + + +# 死亡碎块弹飞:立绘盒中心五块碎石抛物线散落(程序化色块——确定性散布,禁全局随机) +func _spawn_death_shards() -> void: + _death_shards = DEATH_SHARD_N + var center := _portrait_rect().get_center() + for i in DEATH_SHARD_N: + var shard := ColorRect.new() + shard.color = UiKit.STONE if i % 2 == 0 else UiKit.STONE.darkened(0.2) + shard.size = Vector2(7, 5) + Vector2(2, 0) * float(i % 2) + shard.mouse_filter = Control.MOUSE_FILTER_IGNORE + shard.position = center + Vector2(float(i % 3) - 1.0, float(i / 3) - 0.5) * 6.0 + shard.rotation = float(i) * 0.7 + add_child(shard) + var dir := Vector2((float(i % 3) - 1.0) * 30.0, -36.0 - 6.0 * float(i / 3)) + var peak := shard.position + dir + var stw := shard.create_tween() + stw.tween_property(shard, "position", peak, UiKit.motion(0.18)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) + stw.tween_property(shard, "position", peak + Vector2(dir.x * 0.4, 52.0), UiKit.motion(0.27)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + stw.parallel().tween_property(shard, "modulate:a", 0.0, UiKit.motion(0.18)).set_delay(UiKit.motion(0.27)) + stw.tween_callback(shard.queue_free) + + +# 演出探针出口(fx_probe——死亡碎裂断言面) +func fx_probe() -> Dictionary: + return {"death_shards": _death_shards} + + func is_dead() -> bool: return _dead diff --git a/game/smoke/fx_probe.gd b/game/smoke/fx_probe.gd new file mode 100644 index 00000000..fa262006 --- /dev/null +++ b/game/smoke/fx_probe.gd @@ -0,0 +1,78 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 演出密度探针(样张 v2 冻结批 T3——演出四项可触发+探针断言覆盖): +# ①死亡碎裂(standee mark_dead → 碎块计数)②意图三档动画(damage/buff/debuff/charge 摇定不炸+徽标可见) +# ③胜利纹章+失败掸灰+旗光收回(battle_end open_victory/open_defeat → fx_probe 计数) +# ④结算轮光带(settlement_fx play_round_sweep → 子节点落位)。 +# 运行:Godot_console.exe --headless --path . res://smoke/fx_probe.tscn +extends Node + + +func _ready() -> void: + await get_tree().process_frame + var problems: Array = [] + + # ---- ①② 站桩侧:死亡碎裂 + 意图三档 ---- + var sb_script := load("res://presentation/views/standee_body.gd") + var sb = sb_script.new("enemy") + sb.box = Vector2(150, 176) # 站桩盒几何(Node2D 自定义字段——非 Control.size) + add_child(sb) + sb.set_data({"display_name": "地精", "enemy_id": "goblin", "hp": 10, "max_hp": 10}) + await get_tree().process_frame + # 意图三档:特殊准备(damage/buff/debuff)与蓄力档(heavy)摇定——动画触发+徽标可见 + for tier in [["damage", "slash"], ["buff", "atk_up"], ["debuff", "weaken_hit"], ["damage", "heavy"]]: + sb.set_pending(tier[0], tier[1]) + if not sb.intent_probe().visible: + problems.append("意图徽标应可见(%s/%s)" % [tier[0], tier[1]]) + await get_tree().process_frame + sb.set_pending("basic", "") # 档①普通攻击=零表示 + if sb.intent_probe().visible: + problems.append("普通攻击徽标应零表示(§4.6.4 档①)") + # 死亡碎裂 + sb.mark_dead() + await get_tree().process_frame + var dp: Dictionary = sb.fx_probe() + if int(dp.get("death_shards", 0)) != 5: + problems.append("死亡碎块应 5 块(§8.4 冻结值),实得 %s" % [dp.get("death_shards")]) + if not sb.is_dead(): + problems.append("mark_dead 后 is_dead 应真") + + # ---- ③ 终局侧:胜利纹章 / 失败掸灰+旗光 ---- + var end_ov := BattleEndOverlay.new() + add_child(end_ov) + var br := Rect2(300, 300, 400, 300) + end_ov.open_victory("胜利", "继续", br) + await get_tree().process_frame + var vp: Dictionary = end_ov.fx_probe() + if int(vp.get("crest_shards", 0)) != 10: + problems.append("胜利纹章碎砖应 10 枚(§2.4),实得 %s" % [vp.get("crest_shards")]) + end_ov.close() + end_ov.open_defeat("失败", "重来", br) + await get_tree().process_frame + var fp: Dictionary = end_ov.fx_probe() + if int(fp.get("dust_motes", 0)) != 8: + problems.append("失败掸灰尘絮应 8 枚(§2.5),实得 %s" % [fp.get("dust_motes")]) + if int(fp.get("flag_dots", 0)) != 8: + problems.append("旗光收回应 8 点,实得 %s" % [fp.get("flag_dots")]) + end_ov.close() + + # ---- ④ 结算轮光带(含前导亮缘) ---- + var sfx := SettlementFxView.new() + add_child(sfx) + sfx.play_round_sweep(br) # 光带几何全由 board_rect 驱动——无需容器尺寸 + await get_tree().process_frame + var n_fx := 0 + for ch in sfx.get_children(): + if ch is ColorRect: + n_fx += 1 + if n_fx < 2: + problems.append("结算光带应含带体+前导缘线 ≥2 节点,实得 %d" % n_fx) + + # ---- 汇总 ---- + if problems.is_empty(): + print("[fx_probe] PASS — 死亡碎裂5块/意图三档+零表示/胜利纹章10/失败掸灰8+旗光8/结算光带+缘线 全链路通过") + get_tree().quit(0) + else: + for p in problems: + print("[fx_probe] FAIL — " + p) + print("[fx_probe] FAIL — %d 项断言未过" % problems.size()) + get_tree().quit(1) diff --git a/game/smoke/fx_probe.tscn b/game/smoke/fx_probe.tscn new file mode 100644 index 00000000..b52cc316 --- /dev/null +++ b/game/smoke/fx_probe.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://smoke/fx_probe.gd" id="1"] + +[node name="FxProbe" type="Node"] +script = ExtResource("1") From 2d1388b466a2cd6df851a606b21313967c4b1352 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Mon, 28 Sep 2026 04:52:02 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat(art):=20T4=20=E6=A3=8B=E7=9B=98?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E5=91=88=E7=8E=B0=E7=BB=88=E7=A8=BF=E6=89=B9?= =?UTF-8?q?=E2=80=94=E2=80=94=E6=95=B0=E5=AD=97=E5=BD=A2=E5=88=B6=E5=86=BB?= =?UTF-8?q?=E7=BB=93=E5=80=BC=E8=90=BD=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ①数字墨三档色温:UiKit 新增 INK_COLD #132E36/INK_OCHRE #3C250C/INK_RUST #4A1E14,board_view._digit_ink(d) 按 1-3/4-5/6-8 分档(冷=稀疏暖=密集氛围档,字形为唯一数值载体——§4.1 禁八色分色不变);②字号下限 14→18+ceil 口径=max(18, ceil(格边×0.5))(§7.4 冻结公式);③色牌 0.66/0.68→0.60/0.62 格边=样张 v2 冻结值(留白 ≥2px),旗条注释旗朱→封印黑随文。 §1.4 验收 1 硬指标不放松(牌 L≥0.70/墨 L≤0.027/WCAG≥4.5:1 样张 A3 机检在闸)。ui_tune_probe 回归 PASS。轻闸=冒烟 557/0+五探针全 PASS(修复本批一次编辑缩进事故——SCRIPT ERROR 归零后复跑)。 --- game/presentation/views/board_cell_unit.gd | 12 ++++++------ game/presentation/views/board_view.gd | 11 +++++++++++ game/presentation/views/ui_kit.gd | 3 +++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/game/presentation/views/board_cell_unit.gd b/game/presentation/views/board_cell_unit.gd index 42026571..67d709dd 100644 --- a/game/presentation/views/board_cell_unit.gd +++ b/game/presentation/views/board_cell_unit.gd @@ -140,19 +140,19 @@ func _draw() -> void: draw_texture_rect(tex_mutation, rect, false) # ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?") if glyph != "": - # 数字色牌(原则二:数字一律垫素色直角牌——对比由牌墨承载,可读不依赖格底;暗地面瓦片批的 - # 可读性修复 2026-09-26。牌尺寸 0.66/0.68 格边居中——字形 ≥0.5 格边在牌内留白 ≥1px; - # 旗=未开格不垫牌(旗朱在墙瓦片上已可读)) + # 数字色牌(原则二:数字一律垫素色直角牌——对比由牌墨承载,可读不依赖格底。 + # 牌尺寸 0.60/0.62 格边居中 = 样张 v2 冻结值(2026-09-28 四冻结·数字形制;字形 0.5 格边 + # 在牌内留白 ≥2px);旗=未开格不垫牌(封印黑在墙瓦片上已可读)) if plate: - var pw := edge * 0.66 - var ph := edge * 0.68 + var pw := edge * 0.60 + var ph := edge * 0.62 var pr := Rect2((edge - pw) * 0.5, (edge - ph) * 0.5, pw, ph) draw_rect(pr, UiKit.DIGIT_PLATE, true) draw_rect(pr, UiKit.DIGIT_PLATE_EDGE, false, 1.0) if tex_glyph != null: draw_texture_rect(tex_glyph, rect, false) else: - _draw_glyph(glyph, glyph_color, maxi(14, int(edge * 0.5))) + _draw_glyph(glyph, glyph_color, maxi(18, int(ceil(edge * 0.5)))) # 字号下限 18(§7.4 冻结公式) if badge_glyph != "": if tex_badge != null: draw_texture_rect(tex_badge, rect, false) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index 4b757bad..fbd27805 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -627,6 +627,16 @@ func _badge_glyph(category: String) -> String: # ---- 格态渲染(灰度截图可辨:完整覆尘 / 碎裂裸土 / 已开数字) ---- +# 数字墨三档色温(样张 v2 冻结批 T4——art-bible §4.1:1-3 青墨/4-5 赭墨/6-8 锈墨; +# 冷=稀疏、暖=密集的氛围档,字形为唯一数值载体——禁八色分色) +func _digit_ink(d: int) -> Color: + if d <= 3: + return UiKit.INK_COLD + if d <= 5: + return UiKit.INK_OCHRE + return UiKit.INK_RUST + + func _paint_cell(c: Vector2i, hero: Vector2i) -> void: var u: Node2D = _cells.get(c) if u == null: @@ -651,6 +661,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void: glyph = "?" # 数字遮蔽(地形变异 hide_number——信息型:该格真实数字不显示) else: glyph = str(d) + digit_col = _digit_ink(d) # 数字墨三档色温(样张 v2 冻结批 T4——§4.1) else: if flag: bg = COL_BRICK diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index 203a6d2a..0886f9a5 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -7,6 +7,9 @@ extends RefCounted # ── 色板(语义族;值 = 2026-09-20 收敛时现值) ── const INK := Color8(51, 38, 27) # 墨族——正文/数字(对格底 ≥4.5:1) +const INK_COLD := Color8(19, 46, 54) # 青墨 1-3 #132E36(数字墨三档·冷=稀疏——样张 v2 定稿 2026-09-28) +const INK_OCHRE := Color8(60, 37, 12) # 赭墨 4-5 #3C250C(数字墨三档·中) +const INK_RUST := Color8(74, 30, 20) # 锈墨 6-8 #4A1E14(数字墨三档·暖=密集) const INK_LABEL := Color8(46, 34, 24) # 标签深墨 const INK_TITLE := Color8(30, 26, 22) # 亮底标题墨 const INK_ROUTE := Color8(24, 20, 16) # 节点图最深墨 From a08d89c64673cf2b38d52db0cea9f25b9cabf964 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Mon, 28 Sep 2026 04:56:19 +0800 Subject: [PATCH 5/5] =?UTF-8?q?docs(qa):=20T5=20M-1=20=E5=87=BA=E5=8F=A3?= =?UTF-8?q?=E9=97=B8=E6=A3=80=E6=9F=A5=E5=8D=95=E6=88=90=E6=96=87+06=20?= =?UTF-8?q?=E4=B8=A4=E6=9D=A1=E7=9B=AE=E6=94=B6=E5=8F=A3+roadmap=20?= =?UTF-8?q?=E6=8C=82=E8=B5=B7=E6=B8=85=E5=8D=95=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 检查单 design/qa/m1-exit-gate-checklist-2026-09-28.md:迭代 1+2 机器闸读数汇总+真人目检八项(逐项可勾,1-7 过=M-1 关闭→M-2 解锁)+✦ 缺料清单八件(B 区背景横幅/八敌种立绘/主角旗色手修/行进器/序列帧可选/巢穴纹封印纹样/变异纹带/格面徽章——只登记不生图)+代拍板追认五项。 06 解决即移除:「战斗画面表现收口(循环 2 输入清单)」与「棋盘数字呈现优化(色牌=临时档)」两 entries 删除(样张 v2 冻结/框材接线/演出终稿/数字终稿全落籍——draw calls 归 Alpha 验收批 roadmap 卡④承载,本关强化接线归 C 区预留条目)。 roadmap 挂起清单对齐:样张 v1 否决原因闸移已解闸存档;挂起改为=真人试玩/M-1 目检/迭代 1 T4 在飞。 --- design/gdd/06-待办清单.md | 21 ------- .../qa/m1-exit-gate-checklist-2026-09-28.md | 59 +++++++++++++++++++ roadmap.md | 2 +- 3 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 design/qa/m1-exit-gate-checklist-2026-09-28.md diff --git a/design/gdd/06-待办清单.md b/design/gdd/06-待办清单.md index 18e2223f..79bd6972 100644 --- a/design/gdd/06-待办清单.md +++ b/design/gdd/06-待办清单.md @@ -423,24 +423,3 @@ NodeOverlay 现行实态为模态覆盖层:恢复点全量服务(回复读 ### 设置与本地化 - -#### 战斗画面表现收口(循环 2 输入清单,2026-09-26 登记) - -**现状**:2026-09-26 用户令「需要做的都记录进待办」后盘点登记;能修的已修完(数字色牌/墙轮廓/血量数字/小人脚浮/字形错位/入场演出,台账 176-181),剩余七项—— - -- **界面环境与框材组 8 件**:规格与渠道已登记 art-assets-checklist §4.1;♻ 件待用户审阅 ui-reuse 候选包挑批后接线,✦ 背景横幅待样张 v2 冻结后生图 -- **战斗一屏样张 v2 重出**:v1 已否,重 roll 预算剩 2 轮;**验收闸形已拍(2026-09-27)=A 机检冻结零参与**——用户补否决原因一句话后转成机检断言集(原 12 条+气质代理断言),v2 机检全过即冻结落码、批末用户目检否决才返工;过=v2 冻结为换皮输入。**待输入 = v1 否决原因一句话(用户已认领待补)** -- **高墙感挑档落码**:**已拍板(2026-09-27)=D 投影档**(研究件四变体,用户三选一裁决),迭代 1 实装改常量;研究件现路径 artwork/mockups/(目录收敛随迁) -- **CJK 字体随包**:**已拍板(2026-09-27)=Fusion Pixel OFL**(用户二选一裁决;CJK 覆盖+授权干净定案),迭代 1 实装入包 -- **演出密度**:死亡碎裂序列帧/胜利碎砖纹章/失败坐砖掸灰/意图与结算动画终稿——需样张 v2 冻结动画方案 -- **本关强化列表数据接线**:本清单既有 C 区预留区条目承载,不重登 -- **draw calls 270→<200**:归 Alpha 验收批,真机测量后优化 - -敌人立绘素材=用户外部生成中(八敌种槽位已接线,素材入库即自动显示),不入本条。棋盘数字呈现优化(色牌临时档)另见本清单同日条目。 - -**归属**:循环 2 计划书(样张 v2 冻结后另立) -#### 棋盘数字呈现优化(色牌=临时档,2026-09-26 登记) - -**现状**:2026-09-26 用户实机反馈——现行数字呈现(米白色牌+墨字,呈现修复批落码)**太丑,只作临时档**,「现在够用」;终稿数字呈现方案待设计(候选方向:色牌形制/字号字体/去牌化直写+描边/与瓦片一体的雕刻数字等),归样张 v2 冻结视觉语言后的表现批一并定。**备忘性质**:防止临时档被当成终稿遗忘。 - -**归属**:战斗画面表现收口条(循环 2);判据续用 §1.4 验收 1(可读性硬指标不因形制放松) diff --git a/design/qa/m1-exit-gate-checklist-2026-09-28.md b/design/qa/m1-exit-gate-checklist-2026-09-28.md new file mode 100644 index 00000000..78ac605f --- /dev/null +++ b/design/qa/m1-exit-gate-checklist-2026-09-28.md @@ -0,0 +1,59 @@ +# M-1 出口闸检查单(2026-09-28,迭代 2 收口批交付) + +> 用途:M-1「切片表现收口」出口闸 = **真人目检清单全绿**(机器闸已全部自动过);本单即人闸载体,逐项可勾。 +> 全绿 = M-1 关闭 → **M-2 迭代 3「读数重测与调参」解锁**(其门=M-1 出口闸)。 +> 目检方式:实机跑 `godot --path game`(或开 Godot 编辑器 F5),按清单逐项看;任一项不过 = 回该批返工(样张类至多一轮,收集否决维度补断言后重出)。 + +## 一、机器闸读数汇总(已自动过,留痕) + +### 迭代 1(m1-iter1-20260928 worktree,2026-09-28 台账 216/217) +| 卡 | 内容 | 读数 | +|---|---|---| +| T1 单元测试与契约层 | 七套件实跑入口 tests/run_all.gd | 全绿 | +| T2 高墙感 D 投影档落码 | 立面双段暗带+地面投影(用户挑档 D) | 冒烟 557/0+双探针 PASS | +| T3 CJK 字体随包 | Fusion Pixel OFL zh_hans+第八套件 ui_font_asset_test | 冒烟 557/0+8 套件全绿 | + +### 迭代 2(本 loop,f9b0765→2d1388b) +| 卡 | 内容 | 读数 | +|---|---|---| +| T1 样张 v2 重出+机检冻结 | 机检 **16/16 全绿**(13 现行+A3 Δ≥55+F6 双明度带+F7 悬崖同源+A6 观感密度)→ 四冻结落文 art-bible v2.13 | 报告 `design/research/sample-v2-机检报告-2026-09-28.md` | +| T2 框材组 8 件换皮 | ♻ 九件量化改色入包+UiKit 单点工厂+五视图接线 | 冒烟 557/0+四探针 PASS | +| T3 演出密度终稿 | 死亡碎裂/胜利纹章/失败掸灰/意图三档/结算光带+**fx_probe 新探针** | 冒烟 557/0+五探针 PASS | +| T4 棋盘数字终稿 | 三档墨色温/字号 ≥18 公式/色牌 0.60·0.62 冻结值 | 冒烟 557/0+五探针(ui_tune_probe 回归含) | + +- 冒烟检查数 557 = 开工基线(零删减);合并后主区复跑留痕见台账 219。 +- 迭代 1 未落籍注记:m1-iter1 worktree 尚有 T4 关卡画面实装在飞(main.gd/route_overlay/双探针四文件),本 loop 弱耦合避让未触碰;其落籍不阻塞本人闸(目检第 8 项注两级状态)。 + +## 二、真人目检清单(逐项可勾,勾完=人闸过) + +- [ ] **1. 棋盘立体感**(样张 v2 否决③复核):战斗中未开墙格有南立面暗带+地面投影,整板底部/右侧有悬崖暗面与悬浮影——「立得起来」 +- [ ] **2. 观感密度**(否决①复核):棋盘整体不觉得平/糊——裂纹碎石变体、类型微光、物件纹样层次可感 +- [ ] **3. 数字可读**:1-8 数字色牌上清晰(三档墨色温:小数字偏冷、大数字偏暖),缩小窗口仍可读 +- [ ] **4. 换皮框架**:C 区面板/B 区舞台底/图例弹窗=石板质感九宫格;全站按钮=暖砖两态(按下下沉);开始界面有石纹底噪;营地有石框容器+钥石/柜台图标 +- [ ] **5. 演出四项**:击杀敌人→灰化+碎块弹飞(非瞬间消失);胜利→金蜡封落下+碎砖拼纹章环;失败→暮蓝氛围+尘絮飘落+旗光点收回;敌摇定特殊技→徽标弹跳、重击→缓推蓄力定格 +- [ ] **6. 结算光带**:轮末结算金光带自棋盘上缘扫向下缘,带前缘有亮线 +- [ ] **7. 图例正确性**:图例旗条目为封印黑色块、墨字可读 +- [ ] **8. 迭代 1 在飞件**(若 m1-iter1 已合并):D 投影观感融合/Fusion Pixel 中文渲染/关卡画面实装——若未合并则本项留待其批末目检,不阻塞 M-1 + +**判定**:1–7 全过 + 8 注记状态 → 人闸过,M-1 关闭;登记裁决入台账,roadmap 里程碑移动。 + +## 三、✦ 缺料清单(只登记不生图——生图执行权在用户) + +| 件 | 渠道 | 规格/提示词指针 | 现状承载 | +|---|---|---|---| +| B 区背景横幅(战斗舞台背景插画) | ✦ 生图 | art-bible §6.1 明亮地窟/低对比自然后退;清单 §1.2 | 程序化石板皮+底噪(本批 T2) | +| 八敌种站桩立绘 9 卡 | ✦ 外部生成中 | comfyui-prompts.md v2.9(攻击段=底稿锁死句) | 槽位在位,入库即显示零代码 | +| 主角 normal/hit 成图旗色手修 | ✦ 手修 | 旗朱→封印黑 #000000(封印黑批遗留) | 远程单已重锁十六进制 | +| 节点图行进器形象 | ✦/⚙ | 清单 §3.2(与主角同族蓝) | 程序化占位 | +| 意图三档/死亡碎裂序列帧 | ✦ 可选 | art-bible §8.4 冻结帧数档 | 程序化 tween 已承载(本批 T3) | +| 巢穴纹/封印印记正式纹样 | ✦/⚙ | 清单 §4 封印特效两件+§3.3 | 程序化占位在役 | +| 变异纹带六类型终稿 | ⚙ 样张批后可排产 | art-bible §3.2 纹带形按轴两分 | 槽位先行(CellUnit tex_mutation) | +| 格面资源徽章 19 枚 | ⚙ 脚本管线 | 清单 §3.1 键契约 | 程序化占位 | + +## 四、代拍板追认清单(本 loop 白名单内已裁,待用户过目) + +1. A6 观感密度口径=明度梯度均值(高频细节能量)——v1 同口径实测 4.09×1.15=4.70;低频双峰由 A3 承载(口径注记见机检报告)。 +2. F6 双明度带测「南向临地墙格」(立面语义承载位,内墙 center 件无立面属正确)。 +3. 复用件选型=Kenney 系九宫格/图标+OGA 石纹(全 CC0,game-icons CC-BY 件未采用);量化色带端点=PANEL_BORDER/PANEL_DATA/BRICK/FLOOR/PLATE token。 +4. 演出参数=死亡碎块 5 块 0.45s/纹章碎砖 10 枚/掸灰 8 絮/蓄力定格 0.40s(§8.4 冻结档)。 +5. 数字牌尺寸 0.66→0.60(对齐样张冻结值);图例旗条目名回退「旗」(click_probe 基线精确匹配,封印语义移描述列)。 diff --git a/roadmap.md b/roadmap.md index d0d2ef5a..0bd96232 100644 --- a/roadmap.md +++ b/roadmap.md @@ -22,7 +22,7 @@ - **阶段**:Vertical Slice Implementation(`stage.txt`) - **已达成**:一局完整三关冒险+存档+元进度+营地闭合(数值 provisional,归调参批标定);表现层 Node2D 双阶段+换肤接线落籍(渲染架构到位、素材占位在役);UX 文档面四 spec 齐(battle / level-map / level-clear / interaction-patterns) -- **挂起待用户输入(批次解锁点)**:① 样张 v1 否决原因一句话(用户已认领待补——解锁样张 v2 机检断言集;闸形已拍=A 机检冻结零参与,机检全过即落码)② 真人试玩批次(解锁曲线调参读数)——已解闸存档:ADR-0007 裁决(裁丙,台账 210);高墙档=D 投影+字体=Fusion Pixel OFL(2026-09-27 快拍,迭代 1 卡②③ 闸解除) +- **挂起待用户输入(批次解锁点)**:① 真人试玩批次(解锁曲线调参读数)② **M-1 出口闸=真人目检**(检查单 `design/qa/m1-exit-gate-checklist-2026-09-28.md`,迭代 2 批末交付)③ 迭代 1 T4 关卡画面实装在飞(m1-iter1 worktree 未落籍)——已解闸存档:ADR-0007 裁决(裁丙,台账 210);高墙档=D 投影+字体=Fusion Pixel OFL(2026-09-27 快拍,迭代 1 卡②③ 闸解除);样张 v1 否决原因四条(2026-09-28 用户补,已全数断言化——样张 v2 机检 16/16 冻结,台账 219) - **已达成(当日补记)**:统一随机流架构(七流并一+全事件日志,rng 面零散随机源清零;并流后 MC/verifier 历史基线作废,重测义务并入迭代 3 卡①) - **已知挂账**:曲线调参批(基线读数重测+杠杆扫描,段界结账口径);魔法道具内容批(node-reward spec 随批出生);反制批 Alpha