fix(ui): 棋盘小人修正批——用户三裁决落地(2026-09-25):①缩放=整数 1× 档 32×32 盒格内居中(HERO_SPRITE_SCALE 常量定档,cell_edge/32 填充公式作废——圣经 §8.2 禁非整数缩放硬规则+v1.9 注记 71% 占格合规预期,上批「填满格」代拍板系误判);②挥铲=握把锚 (18,12) 对齐英雄持握手位(以手为圆心、铲长为半径旋转,手=盒底往上 1/4 盒高·水平居中——hero_hand_point/hero_sprite_scale 出口,共享边中点+12% 偏置作废);③触发=格子点开那一刻确认(settle 同步受理后 refresh 同拍,措辞归位非代码变更);冒烟 557/0+ui_input/click/layout 三探针绿

This commit is contained in:
DKChan committed 2026-09-25 05:47:43 +08:00
1 parent b0880919ee
commit 65325d111d
3 files changed
+38 -18

No files matched your search

+4 -3
View File
@@ -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 前置告知
+8 -10
View File
@@ -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]:
+26 -5
View File
@@ -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()