From 65325d111d5e5f50c57dcc5db17967bf98255416 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Fri, 25 Sep 2026 05:47:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E6=A3=8B=E7=9B=98=E5=B0=8F=E4=BA=BA?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=89=B9=E2=80=94=E2=80=94=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=B8=89=E8=A3=81=E5=86=B3=E8=90=BD=E5=9C=B0=EF=BC=882026-09-2?= =?UTF-8?q?5=EF=BC=89=EF=BC=9A=E2=91=A0=E7=BC=A9=E6=94=BE=3D=E6=95=B4?= =?UTF-8?q?=E6=95=B0=201=C3=97=20=E6=A1=A3=2032=C3=9732=20=E7=9B=92?= =?UTF-8?q?=E6=A0=BC=E5=86=85=E5=B1=85=E4=B8=AD=EF=BC=88HERO=5FSPRITE=5FSC?= =?UTF-8?q?ALE=20=E5=B8=B8=E9=87=8F=E5=AE=9A=E6=A1=A3=EF=BC=8Ccell=5Fedge/?= =?UTF-8?q?32=20=E5=A1=AB=E5=85=85=E5=85=AC=E5=BC=8F=E4=BD=9C=E5=BA=9F?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=9C=A3=E7=BB=8F=20=C2=A78.2=20=E7=A6=81?= =?UTF-8?q?=E9=9D=9E=E6=95=B4=E6=95=B0=E7=BC=A9=E6=94=BE=E7=A1=AC=E8=A7=84?= =?UTF-8?q?=E5=88=99+v1.9=20=E6=B3=A8=E8=AE=B0=2071%=20=E5=8D=A0=E6=A0=BC?= =?UTF-8?q?=E5=90=88=E8=A7=84=E9=A2=84=E6=9C=9F=EF=BC=8C=E4=B8=8A=E6=89=B9?= =?UTF-8?q?=E3=80=8C=E5=A1=AB=E6=BB=A1=E6=A0=BC=E3=80=8D=E4=BB=A3=E6=8B=8D?= =?UTF-8?q?=E6=9D=BF=E7=B3=BB=E8=AF=AF=E5=88=A4=EF=BC=89=EF=BC=9B=E2=91=A1?= =?UTF-8?q?=E6=8C=A5=E9=93=B2=3D=E6=8F=A1=E6=8A=8A=E9=94=9A=20(18,12)=20?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E8=8B=B1=E9=9B=84=E6=8C=81=E6=8F=A1=E6=89=8B?= =?UTF-8?q?=E4=BD=8D=EF=BC=88=E4=BB=A5=E6=89=8B=E4=B8=BA=E5=9C=86=E5=BF=83?= =?UTF-8?q?=E3=80=81=E9=93=B2=E9=95=BF=E4=B8=BA=E5=8D=8A=E5=BE=84=E6=97=8B?= =?UTF-8?q?=E8=BD=AC=EF=BC=8C=E6=89=8B=3D=E7=9B=92=E5=BA=95=E5=BE=80?= =?UTF-8?q?=E4=B8=8A=201/4=20=E7=9B=92=E9=AB=98=C2=B7=E6=B0=B4=E5=B9=B3?= =?UTF-8?q?=E5=B1=85=E4=B8=AD=E2=80=94=E2=80=94hero=5Fhand=5Fpoint/hero=5F?= =?UTF-8?q?sprite=5Fscale=20=E5=87=BA=E5=8F=A3=EF=BC=8C=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E8=BE=B9=E4=B8=AD=E7=82=B9+12%=20=E5=81=8F=E7=BD=AE=E4=BD=9C?= =?UTF-8?q?=E5=BA=9F=EF=BC=89=EF=BC=9B=E2=91=A2=E8=A7=A6=E5=8F=91=3D?= =?UTF-8?q?=E6=A0=BC=E5=AD=90=E7=82=B9=E5=BC=80=E9=82=A3=E4=B8=80=E5=88=BB?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=EF=BC=88settle=20=E5=90=8C=E6=AD=A5=E5=8F=97?= =?UTF-8?q?=E7=90=86=E5=90=8E=20refresh=20=E5=90=8C=E6=8B=8D=EF=BC=8C?= =?UTF-8?q?=E6=8E=AA=E8=BE=9E=E5=BD=92=E4=BD=8D=E9=9D=9E=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=EF=BC=89=EF=BC=9B=E5=86=92=E7=83=9F=20557/0+?= =?UTF-8?q?ui=5Finput/click/layout=20=E4=B8=89=E6=8E=A2=E9=92=88=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/main.gd | 7 +++--- game/presentation/views/board_fx_view.gd | 18 ++++++-------- game/presentation/views/board_view.gd | 31 ++++++++++++++++++++---- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index c4d7d42c..59920f0e 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -686,10 +686,11 @@ func _on_board_action(cell: Vector2i, kind: String) -> void: if accepted and kind == "dig" and settlement != null and settlement.minesweeper.current_board() != null and settlement.minesweeper.clear_map_cleared(): board_zone.fx.play_clear_burst() if accepted and kind == "dig" and settlement != null and settlement.minesweeper != null: - # 挖掘演出(棋盘小人贴图批 2026-09-25):挥铲三帧 + 角色弹跳——受理后一拍触发(挖掘不移动英雄, - # 出手方向 = 目标格−英雄格;refresh 同拍重绘零冲突) + # 挖掘演出(棋盘小人贴图批 2026-09-25):挥铲三帧 + 角色弹跳——触发=格子点开那一刻 + #(2026-09-25 用户裁决;settle_batch 同步受理即状态已变,动画纯表现与 refresh 同拍零竞态; + # 挖掘不移动英雄,出手方向 = 目标格−英雄格,圆心=英雄持握手位) var hero_cell: Vector2i = settlement.minesweeper.hero_pos() - board_zone.fx.play_shovel_swing(board_view.cell_origin(hero_cell), cell - hero_cell, board_view.cell_size) + board_zone.fx.play_shovel_swing(board_view.hero_hand_point(), cell - hero_cell, board_view.hero_sprite_scale()) board_view.play_hero_dig_bounce() elif accepted and kind == "move": board_view.notify_hero_move() # 原地移动(目标=当前格幂等受理)帧闪回执——refresh diff 前置告知 diff --git a/game/presentation/views/board_fx_view.gd b/game/presentation/views/board_fx_view.gd index 12fdfa6f..bcd3edbd 100644 --- a/game/presentation/views/board_fx_view.gd +++ b/game/presentation/views/board_fx_view.gd @@ -125,15 +125,16 @@ func play_flag_hit_projectile(heavy: bool) -> void: const SHOVEL_FRAME_PX := 32.0 # 铲帧画布边长(缩放比与英雄帧同源) const SHOVEL_FRAME_S := 0.08 # ~80ms/帧(f0→f1→f2 共 ~240ms 即收) -const SHOVEL_GRIP := Vector2(18, 12) # 握把锚点(素材侧锁定的画布坐标——出手位对齐点) +const SHOVEL_GRIP := Vector2(18, 12) # 握把锚点(素材侧锁定的画布坐标——英雄持握手位对齐点) var _shovel_texs: Array = [] # 惰性装载缓存(素材缺位 = 空表静默回退——素材不入 git) -# 挖掘挥铲:origin = 英雄格左上(board_area 坐标系——BoardView.cell_origin 出口,fx 与棋盘同层同系); -# dir = 挖掘方向单位格向量(素材铲头朝左——dir.x>0 翻转);edge = 格边长(缩放 = edge/32 与角色一致)。 -# 握把锚对齐英雄格与目标格共享边中点(出手位),铲头弧线自然落进目标格。 -func play_shovel_swing(origin: Vector2, dir: Vector2i, edge: float) -> void: +# 挖掘挥铲:hand = 英雄持握手位(BoardView.hero_hand_point 出口,fx 与棋盘同层同系)—— +# 以手为圆心、铲长为半径旋转(2026-09-25 用户裁决;三帧系素材侧预烤的旋转相位,握把锚 (18,12) +# 三帧同位,锚点钉死手位即成弧);dir = 挖掘方向单位格向量(素材铲头朝左——dir.x>0 翻转); +# sprite_scale 与英雄精灵同档(整数 1×——原「共享边中点+12% 偏置」定位与 edge/32 缩放作废)。 +func play_shovel_swing(hand: Vector2, dir: Vector2i, sprite_scale: float) -> void: if _shovel_texs.is_empty(): for p in ["res://assets/art/fx/shovel_swing_f0.png", "res://assets/art/fx/shovel_swing_f1.png", @@ -142,20 +143,17 @@ func play_shovel_swing(origin: Vector2, dir: Vector2i, edge: float) -> void: if t == null: return # 素材缺位——静默回退(fx 层占位演出不设断言面) _shovel_texs.append(t) - var s := edge / SHOVEL_FRAME_PX var flip := dir.x > 0 var grip := SHOVEL_GRIP if flip: grip.x = SHOVEL_FRAME_PX - SHOVEL_GRIP.x # flip_h 围绕纹理中心镜像——锚点 x 随镜 - var deliver := origin + Vector2(edge, edge) * 0.5 \ - + Vector2(dir) * (edge * 0.5 + edge * 0.12) # 共享边中点再向目标格偏 12% 格宽 var sp := Sprite2D.new() sp.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST sp.centered = true sp.texture = _shovel_texs[0] sp.flip_h = flip - sp.scale = Vector2.ONE * s - sp.position = deliver + (Vector2(SHOVEL_FRAME_PX, SHOVEL_FRAME_PX) * 0.5 - grip) * s + sp.scale = Vector2.ONE * sprite_scale + sp.position = hand + (Vector2(SHOVEL_FRAME_PX, SHOVEL_FRAME_PX) * 0.5 - grip) * sprite_scale add_child(sp) var tw := sp.create_tween() for i in [1, 2]: diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index d10ba047..3f34c0c7 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -57,6 +57,10 @@ 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_MOVE_S := 0.14 # 位移时长(~140ms) const HERO_STEP_S := 0.18 # 走路帧周期 = 原地撞墙帧闪时长(~180ms) const HERO_DIG_S := 0.15 # 挖掘弹跳总时长(~150ms,半程压半程回) @@ -251,8 +255,8 @@ func _update_hero(hero: Vector2i) -> void: return if _world.hero == null: var sp: Sprite2D = _world.ensure_hero(_hero_tex_idle) - sp.scale = Vector2.ONE * (_cell / HERO_FRAME_PX) - sp.position = Vector2(hero) * (_cell + float(SEP)) + Vector2(_cell * 0.5, float(_cell)) + sp.scale = Vector2.ONE * HERO_SPRITE_SCALE + sp.position = _hero_feet(hero) _hero_last = hero # 首建/随 rebuild 重建——即时落位无演出 return if hero == _hero_last and not _hero_bump_pending: @@ -273,8 +277,8 @@ func _place_hero(from: Vector2i, to: Vector2i, instant: bool) -> void: var sp: Sprite2D = _world.hero if sp == null: return - var s := _cell / HERO_FRAME_PX - var feet := Vector2(to) * (_cell + float(SEP)) + Vector2(_cell * 0.5, float(_cell)) + var s := HERO_SPRITE_SCALE + var feet := _hero_feet(to) if _hero_move_tw != null and _hero_move_tw.is_valid(): _hero_move_tw.kill() if instant: @@ -322,7 +326,7 @@ func play_hero_dig_bounce() -> void: return if _hero_dig_tw != null and _hero_dig_tw.is_valid(): _hero_dig_tw.kill() - var s := _cell / HERO_FRAME_PX + var s := HERO_SPRITE_SCALE sp.scale = Vector2(s, s) _hero_dig_tw = create_tween() _hero_dig_tw.tween_property(sp, "scale:y", s * HERO_BOUNCE, UiKit.motion(HERO_DIG_S * 0.5)).set_trans(Tween.TRANS_SINE) @@ -335,6 +339,23 @@ func cell_origin(c: Vector2i) -> Vector2: return _svp_container.position + Vector2(c) * (_cell + float(SEP)) +# 英雄 32×32 盒格内居中的脚底锚点(盒底中点——弹跳 scale.y 向此收敛) +func _hero_feet(c: Vector2i) -> Vector2: + var m := (_cell - HERO_FRAME_PX) * 0.5 + return cell_origin(c) + Vector2(m + HERO_FRAME_PX * 0.5, m + HERO_FRAME_PX) + + +# 英雄持握手位(fx 同层坐标系)——挥铲特效圆心(2026-09-25 用户裁决:以手为圆心、铲长为半径 +# 旋转;手 = 盒底往上 1/4 盒高、水平居中) +func hero_hand_point() -> Vector2: + return _hero_feet(_hero_last) + Vector2(0.0, -HERO_FRAME_PX * 0.25) + + +# 英雄精灵缩放档(fx 挥铲同档出口——两演出比例恒一致) +func hero_sprite_scale() -> float: + return HERO_SPRITE_SCALE + + # 真实输入路径(行为探针复用):合成点击 → 与真实 gui_input 同一翻译出口 func click_cell(cell: Vector2i, button: int) -> void: var ev := InputEventMouseButton.new()