From 6cbb3a566f5c5e703cd54b8e1f010e32c4ec2d16 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Fri, 25 Sep 2026 06:19:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E5=B0=8F=E4=BA=BA=E7=AB=8B?= =?UTF-8?q?=E4=BD=93=E6=84=9F=E6=89=B9+=E7=B4=A0=E6=9D=90=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=85=B1=E8=AF=86=EF=BC=882026-09-26=20=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BA=8C=E8=A3=81/=E7=AB=8B=E7=BA=A6=EF=BC=89?= =?UTF-8?q?=EF=BC=9A=E2=91=A0=E6=A3=8B=E7=9B=98=E5=B0=8F=E4=BA=BA=201.5?= =?UTF-8?q?=C3=97=20=E6=A1=A3=EF=BC=88HERO=5FSPRITE=5FSCALE=201.0=E2=86=92?= =?UTF-8?q?1.5=EF=BC=8C=E6=98=8E=E7=A4=BA=E8=A3=81=E5=86=B3=E5=8E=8B=20?= =?UTF-8?q?=C2=A78.2=20=E4=B8=80=E8=88=AC=E8=A7=84=E5=88=99=E2=80=94?= =?UTF-8?q?=E2=80=94=E8=B1=81=E5=85=8D=E6=B3=A8=E8=AE=B0=E5=BD=92=20art-bi?= =?UTF-8?q?ble=20=E6=89=B9=EF=BC=89+=E8=84=9A=E9=94=9A=E8=90=BD=E6=A0=BC?= =?UTF-8?q?=E9=AB=98=2075%=20=E7=BA=BF=EF=BC=88HERO=5FGROUND=5FFRAC?= =?UTF-8?q?=EF=BC=8C=E3=80=8C=E8=84=9A=E8=B8=A9=E6=A0=BC=E5=AD=90=201/4=20?= =?UTF-8?q?=E5=A4=84=C2=B7=E8=87=AA=E5=BA=95=E8=BE=B9=E9=87=8F=E3=80=8D?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=B8=8D=E5=AE=8C=E5=85=A8=E5=B1=85=E4=B8=AD?= =?UTF-8?q?+=E5=A4=B4=E8=B6=8A=E4=B8=8A=E6=A0=BC=E6=B2=BF=E9=80=A0?= =?UTF-8?q?=E7=AB=8B=E4=BD=93=EF=BC=8C=E9=94=9A=E7=82=B9=E4=B8=8E=E7=BC=A9?= =?UTF-8?q?=E6=94=BE=E8=A7=A3=E8=80=A6=EF=BC=89=EF=BC=9B=E2=91=A1=E8=84=9A?= =?UTF-8?q?=E4=B8=8B=E6=A4=AD=E5=9C=86=E5=BD=B1=3DGodot=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=90=88=E6=88=90=EF=BC=88GradientTexture2D=20?= =?UTF-8?q?=E5=BE=84=E5=90=91=E6=B8=90=E5=8F=98=C3=97=E9=BB=91=E8=89=B2=20?= =?UTF-8?q?modulate=20=E5=8E=8B=E6=89=81=EF=BC=8C=E6=8C=82=E8=8B=B1?= =?UTF-8?q?=E9=9B=84=E5=AD=90=E4=BB=B6=20z=3D-1=20=E9=9A=8F=E7=88=B6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E4=BD=8D=E7=A7=BB/=E7=BF=BB=E8=BD=AC/?= =?UTF-8?q?=E5=BC=B9=E8=B7=B3=EF=BC=8CLINEAR=20=E4=BF=9D=E6=B8=90=E5=8F=98?= =?UTF-8?q?=E5=B9=B3=E6=BB=91=EF=BC=8C=E8=B4=B4=E5=9B=BE=E9=9B=B6=E6=94=B9?= =?UTF-8?q?=E5=8A=A8=EF=BC=89+=E8=8B=B1=E9=9B=84/=E5=BD=B1=E8=A1=A5?= =?UTF-8?q?=E6=8C=82=20use=5Fparent=5Fmaterial=EF=BC=88=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E6=9C=9F=E9=9A=8F=E4=B8=96=E7=95=8C=E5=8E=BB=E9=A5=B1=E5=92=8C?= =?UTF-8?q?=E4=B8=8E=E6=A0=BC=E9=9D=A2=E5=90=8C=E5=8F=A3=E5=BE=84=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=9F=BA=E6=89=B9=E6=BC=8F=E6=8C=82=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E2=91=A2=E7=B4=A0=E6=9D=90=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=88=86=E5=B7=A5=E7=BA=A6=E5=AE=9A=20design/art/asset-path-ma?= =?UTF-8?q?p.md=20=E8=90=BD=E7=9B=98=EF=BC=88candidates=3D=E6=BA=90?= =?UTF-8?q?=E6=A1=A3=E7=A6=81=E8=BF=90=E8=A1=8C=E6=97=B6=E5=BC=95=E7=94=A8?= =?UTF-8?q?/game-assets-art=3D=E5=94=AF=E4=B8=80=E8=90=BD=E5=9C=B0?= =?UTF-8?q?=E5=A4=84/ui-icons=3D=E5=B7=B2=E5=85=A5=E5=BA=93=E7=89=B9?= =?UTF-8?q?=E4=BE=8B/mockups=3D=E8=AF=84=E5=AE=A1=E4=BA=A7=E7=89=A9?= =?UTF-8?q?=EF=BC=9B=E7=B4=A0=E6=9D=90=E6=B5=81=E4=BA=94=E6=AD=A5+?= =?UTF-8?q?=E7=A6=81=20preload+=E5=9F=BA=E5=87=86=E6=9C=9D=E5=90=91/?= =?UTF-8?q?=E9=94=9A=E7=82=B9=E5=A4=87=E6=9F=A5=EF=BC=89=EF=BC=9B=E2=91=A3?= =?UTF-8?q?ui=5Finput=5Fprobe=20case=E2=91=A6=20=E6=9C=9F=E6=9C=9B?= =?UTF-8?q?=E5=80=BC=E9=9A=8F=E8=84=9A=E9=94=9A=E5=85=AC=E5=BC=8F=E6=8D=A2?= =?UTF-8?q?=E6=8C=A1=EF=BC=9B=E5=86=92=E7=83=9F=20557/0+=E4=B8=89=E6=8E=A2?= =?UTF-8?q?=E9=92=88=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design/art/asset-path-map.md | 35 ++++++++++++++++++++++++++ game/presentation/views/board_view.gd | 30 +++++++++++++--------- game/presentation/views/board_world.gd | 29 +++++++++++++++++++-- game/smoke/ui_input_probe.gd | 11 ++++---- 4 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 design/art/asset-path-map.md diff --git a/design/art/asset-path-map.md b/design/art/asset-path-map.md new file mode 100644 index 00000000..d6c6c342 --- /dev/null +++ b/design/art/asset-path-map.md @@ -0,0 +1,35 @@ +# 素材路径分工约定(asset-path-map) + +- 定立:2026-09-26(用户立约指令「把哪些路径、哪些位置是干什么的确定好」——台账 161) +- 效力:全仓素材批的路径判据单点;新素材批开工前对照,违例=实施缺陷 + +## 1. 目录分工总表 + +| 路径 | 角色 | 入 git | 游戏运行时引用 | +|---|---|---|---| +| `design/art/candidates/**` | 设计侧源档(母版 PNG/.pxo/GIF/对比图/验收留底)——**素材的原产地与档案库,游戏禁直接引用** | 否(`.gitignore` 三行规则;`*.md` 指引例外) | **禁**(res:// 路径禁指 candidates) | +| `game/assets/art/**` | **游戏运行时素材唯一落地处**(res://assets/art/...)——定稿素材从 candidates 拷贝入此,改名按圣经 §8.3 | 否(untracked,勿 git add;worktree 间需脚本拷贝同步) | **唯一合法引用源** | +| `game/assets/ui/icons/**` | UI 图标(状态图标批特例——唯一已提交入 git 的素材目录) | **是**(2026-09-24 批特例) | 是 | +| `production/mockups/**` | 混合样张/版式对比图(设计评审产物,非游戏素材) | 否 | 禁 | + +## 2. 素材流(新素材批标准路径) + +1. **产出/候选**:candidates 对应子目录(如 `hero-idle/`) +2. **验收定稿**:用户拍板(候选目录留档不动) +3. **入包**:拷贝 → `game/assets/art/<语义子目录>/`,改名 `§8.3` 域键式(如 `character_hero_portrait.png`) +4. **接线**:代码 `res://assets/art/...` 引用(路径常量单点归 `UiKit.TEX_*`);装载一律 `UiKit.load_tex`(exists 守卫——素材不入 git,缺位树回退),**禁 preload** +5. **提交**:只提交代码/文档;PNG 与 `.import`(全局忽略)均不入 git + +## 3. game/assets/art 语义子目录 + +| 子目录 | 内容 | 现役 | +|---|---|---| +| `character/` | 角色棋盘帧 + B 区立绘 | character_hero_idle/walk(棋盘 32×32 朝左基准)、character_hero_portrait(B 区立绘 48×48) | +| `fx/` | 特效帧 | shovel_swing_f0/f1/f2(挥铲三帧,握把锚 (18,12)) | +| `board/` | 砖面/墙/地板(beast-cave 复用包,地砖复用批) | board_floor/wall/pillar_dungeon_* | + +## 4. 坐标/朝向等工程侧约定备查 + +- 素材基准朝向:角色/铲帧一律朝**左**入库,右向=运行时 `flip_h`(镜像帧不入包,right 变体留 candidates) +- 挥铲握把锚:画布 (18,12)(三帧同位——以手为圆心旋转的预烤相位) +- 缩放纪律:圣经 §8.2 整数缩放为一般规则;棋盘小人 1.5× 系 2026-09-26 用户明示裁决(豁免注记归 art-bible 批另落) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index 1b4dda3b..34c30961 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -57,14 +57,17 @@ const DEPTH_BEVEL := 0.6 # 顶面倒角强度 # 英雄单位演出参数(棋盘小人贴图批 2026-09-25——计划书 §3.B/C 时长档) const HERO_FRAME_PX := 32.0 # 英雄帧画布边长(素材 32×32 锁定——缩放比分母) -const HERO_SPRITE_SCALE := 1.0 # 整数 1× 档、32×32 盒格内居中(2026-09-25 用户裁决:不要求覆盖 - # 格子——圣经 §8.2「禁非整数缩放」硬规则,v1.9 注记 45px 格 1× 占 - # ≈71% 合规;格边 <32 板溢出 ≤(32−cell)px 接受;计划书原 cell_edge/32 - # 填充公式作废) +const HERO_SPRITE_SCALE := 1.5 # 1.5× 档(2026-09-26 用户二裁:实机看 1× 偏小放大 1.5 倍——明示 + # 裁决压圣经 §8.2「禁非整数缩放」一般规则,§8.2 豁免注记归 art-bible + # 批另落;头可越格上沿不裁剪) +const HERO_GROUND_FRAC := 0.75 # 脚锚纵向落点 = 格高 75%(自格顶起;即「脚在格子 1/4 处」自底边量—— + # 2026-09-26 用户裁决:不完全居中、脚踩 1/4 线,头越上格沿造立体感) const HERO_MOVE_S := 0.14 # 位移时长(~140ms) const HERO_STEP_S := 0.18 # 走路帧周期 = 原地撞墙帧闪时长(~180ms) const HERO_DIG_S := 0.15 # 挖掘弹跳总时长(~150ms,半程压半程回) const HERO_BOUNCE := 0.82 # 弹跳压缩比(scale.y 1→0.82→1 脚底收敛) +const HERO_SHADOW_PX := Vector2(32, 12) # 脚下椭圆影屏幕尺寸(Godot 组件合成——径向渐变纹理压扁, + # 贴图零改动,2026-09-26 用户裁决走引擎侧) var _wrap: CenterContainer var _svp_container: SubViewportContainer @@ -254,7 +257,7 @@ func _update_hero(hero: Vector2i) -> void: if _hero_tex_idle == null: return if _world.hero == null: - var sp: Sprite2D = _world.ensure_hero(_hero_tex_idle) + var sp: Sprite2D = _world.ensure_hero(_hero_tex_idle, HERO_SHADOW_PX, HERO_SPRITE_SCALE) sp.scale = Vector2.ONE * HERO_SPRITE_SCALE sp.position = _hero_feet(hero) _hero_last = hero # 首建/随 rebuild 重建——即时落位无演出 @@ -339,18 +342,21 @@ func cell_origin(c: Vector2i) -> Vector2: return _svp_container.position + Vector2(c) * (_cell + float(SEP)) -# 英雄 32×32 盒格内居中的脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距, -# 禁带壳层居中偏移。2026-09-26 修正:上批误用壳层 cell_origin(含 _svp_container.position 居中 -# 偏移)定位世界内精灵,实测整体右偏 ~7 格+偏离格心——实机截图用户报缺陷) +# 英雄脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距,禁带壳层居中偏移。 +# 2026-09-26 修正:上批误用壳层 cell_origin(含 _svp_container.position 居中偏移)定位世界内精灵, +# 实测整体右偏 ~7 格+偏离格心——实机截图用户报缺陷)。 +# 纵向 = 格高 75% 线(脚踩格子 1/4 处·自底边量——2026-09-26 用户二裁);横向格中线。身体向上延伸, +# 头越上格沿不裁剪(立体感裁决);锚点与精灵缩放解耦(缩放只改身体向上长多少)。 func _hero_feet(c: Vector2i) -> Vector2: - var m := (_cell - HERO_FRAME_PX) * 0.5 - return Vector2(c) * (_cell + float(SEP)) + Vector2(m + HERO_FRAME_PX * 0.5, m + HERO_FRAME_PX) + return Vector2(c) * (_cell + float(SEP)) + Vector2(_cell * 0.5, _cell * HERO_GROUND_FRAC) # 英雄持握手位(fx 同层坐标系 = 壳层坐标——fx 在 SubViewport 之外,须含 _svp_container.position -# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中(2026-09-25 用户裁决) +# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中(2026-09-25 用户裁决; +# 盒高随 HERO_SPRITE_SCALE——手位钉在身体上不随缩放漂移) func hero_hand_point() -> Vector2: - return _svp_container.position + _hero_feet(_hero_last) + Vector2(0.0, -HERO_FRAME_PX * 0.25) + return _svp_container.position + _hero_feet(_hero_last) \ + + Vector2(0.0, -HERO_FRAME_PX * 0.25 * HERO_SPRITE_SCALE) # 英雄精灵缩放档(fx 挥铲同档出口——两演出比例恒一致) diff --git a/game/presentation/views/board_world.gd b/game/presentation/views/board_world.gd index ac077d8f..fbdcff47 100644 --- a/game/presentation/views/board_world.gd +++ b/game/presentation/views/board_world.gd @@ -18,17 +18,42 @@ var hero: Sprite2D = null # 英雄单位精灵(棋盘小人贴图批 2026- # 英雄精灵惰性创建(幂等):脚底锚(offset 上移半高——scale.y 弹跳向脚底收敛不悬空)+ z_index=1 # 压格面之上(挥铲特效在 SubViewport 之外恒更上层,满足「格面之上、特效之下」叠序);NEAREST = -# SubViewport 缩放下像素清晰(默认 Linear 会糊)。返回值便于 BoardView 即时落位。 -func ensure_hero(tex: Texture2D) -> Sprite2D: +# SubViewport 缩放下像素清晰(默认 Linear 会糊);use_parent_material = 随世界去饱和材质灰化 +#(结算期与 CellUnit 同口径——基批漏挂,本批补齐)。返回值便于 BoardView 即时落位。 +# 脚下椭圆影(2026-09-26 用户裁决走 Godot 组件合成、贴图零改动):径向渐变纹理 ×黑色半透明 +# modulate 压扁,挂英雄子件(z_index=-1 相对回落格面之上、英雄之下——位移/翻转/弹跳随父件 +# 同步免费拿;尺寸换算归 BoardView.HERO_SHADOW_PX 常量)。纹理 filter 用 LINEAR 保渐变平滑 +#(NEAREST 会把渐变打回条带)。 +func ensure_hero(tex: Texture2D, shadow_px: Vector2, scale: float) -> Sprite2D: if hero != null: return hero + var grad := Gradient.new() + grad.set_color(0, Color(1, 1, 1, 1)) + grad.set_color(1, Color(1, 1, 1, 0)) + var shadow_tex := GradientTexture2D.new() + shadow_tex.gradient = grad + shadow_tex.width = 64 + shadow_tex.height = 64 + shadow_tex.fill = GradientTexture2D.FILL_RADIAL + shadow_tex.fill_from = Vector2(0.5, 0.5) + shadow_tex.fill_to = Vector2(0.5, 0.0) + var shadow := Sprite2D.new() + shadow.texture = shadow_tex + shadow.modulate = Color(0, 0, 0, 0.35) + shadow.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR + shadow.scale = shadow_px / (64.0 * scale) # 子件缩放叠父件 scale——目标屏幕尺寸 ÷ (纹理边 × 父档) + shadow.position = Vector2(0.0, 2.0) # 脚线下 2px——贴地接触感 + shadow.z_index = -1 # 相对父件回落:格面(z=0 树序在先)之上、英雄本体之下 + shadow.use_parent_material = true # 随英雄链世界去饱和材质 hero = Sprite2D.new() hero.texture = tex hero.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST hero.centered = true hero.offset = Vector2(0.0, -16.0) # 纹理中点上移半高——position = 脚底中点 hero.z_index = 1 + hero.use_parent_material = true add_child(hero) + hero.add_child(shadow) return hero # ── 立体悬崖(BoardView.set_cell_area / _update_world_size 单点驱动;厚度定稿 8px=中档 2026-09-24)── diff --git a/game/smoke/ui_input_probe.gd b/game/smoke/ui_input_probe.gd index 790a74aa..eb26fa97 100644 --- a/game/smoke/ui_input_probe.gd +++ b/game/smoke/ui_input_probe.gd @@ -122,15 +122,14 @@ func _ready() -> void: if not ms.is_revealed(dig_t): _problems.append("case⑥ 核心揭格未落位(目标=%s)" % dig_t) # ---- case⑦ 英雄精灵世界落位(棋盘小人贴图批回归闸 2026-09-26):精灵在 SubViewport 世界内, - # 落位 = 格×步距 + 居中脚底锚(禁带壳层 _svp_container.position 居中偏移——上批误用壳层 - # cell_origin 实测右偏 ~7 格+偏离格心,实机截图用户报缺陷)。素材缺位树(hero==null, - # 字形「人」回退在役)跳过;断言前等 0.3s 墙钟——headless 帧率无上限,帧数等待 < 位移 - # Tween 140ms 时会采到半途位置(首跑实证),必须按墙钟等到位。 ---- + # 落位 = 格×步距 + 脚底锚(横=格中线/纵=格高 75% 线——2026-09-26 二裁「脚踩格子 1/4 处」; + # 禁带壳层 _svp_container.position 居中偏移——上批误用壳层 cell_origin 实测右偏 ~7 格+偏离 + # 格心,实机截图用户报缺陷)。素材缺位树(hero==null,字形「人」回退在役)跳过;断言前等 + # 0.3s 墙钟——headless 帧率无上限,帧数等待 < 位移 Tween 140ms 时会采到半途位置(首跑实证)。 ---- if bv._world.hero != null: await get_tree().create_timer(0.3).timeout - var hm: float = (bv.cell_size - bv.HERO_FRAME_PX) * 0.5 var expect: Vector2 = Vector2(ms.hero_pos()) * (bv.cell_size + float(bv.SEP)) \ - + Vector2(hm + bv.HERO_FRAME_PX * 0.5, hm + bv.HERO_FRAME_PX) + + Vector2(bv.cell_size * 0.5, bv.cell_size * bv.HERO_GROUND_FRAC) if bv._world.hero.position.distance_to(expect) > 0.5: _problems.append("case⑦ 英雄精灵世界落位偏移 pos=%s expect=%s" % [str(bv._world.hero.position), str(expect)]) if _problems.is_empty():