From ae8c5bf1c907f27a2d14c47eb7276d8faa384e99 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Fri, 25 Sep 2026 06:09:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E8=8B=B1=E9=9B=84=E7=B2=BE=E7=81=B5?= =?UTF-8?q?=E4=B8=96=E7=95=8C=E8=90=BD=E4=BD=8D=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D+B=E5=8C=BA=E7=AB=8B=E7=BB=98=E6=8D=A2=E8=A3=85?= =?UTF-8?q?=E6=89=B9=EF=BC=882026-09-26=20=E7=94=A8=E6=88=B7=E5=AE=9E?= =?UTF-8?q?=E6=9C=BA=E6=88=AA=E5=9B=BE/=E7=82=B9=E5=90=8D=EF=BC=89?= =?UTF-8?q?=EF=BC=9A=E2=91=A0=5Fhero=5Ffeet=20=E6=94=B9=E4=B8=96=E7=95=8C?= =?UTF-8?q?=E5=9D=90=E6=A0=87=EF=BC=88SubViewport=20=E5=86=85=E6=A0=BC?= =?UTF-8?q?=E5=8E=9F=E7=82=B9=3D=E6=A0=BC=C3=97=E6=AD=A5=E8=B7=9D=EF=BC=9B?= =?UTF-8?q?=E4=B8=8A=E6=89=B9=E8=AF=AF=E7=94=A8=E5=A3=B3=E5=B1=82=20cell?= =?UTF-8?q?=5Forigin=20=E5=90=AB=20=5Fsvp=5Fcontainer.position=20=E5=B1=85?= =?UTF-8?q?=E4=B8=AD=E5=81=8F=E7=A7=BB=3D=E5=AE=9E=E6=B5=8B=E5=8F=B3?= =?UTF-8?q?=E5=81=8F=20~7=20=E6=A0=BC+=E5=81=8F=E7=A6=BB=E6=A0=BC=E5=BF=83?= =?UTF-8?q?=EF=BC=89=EF=BC=9Bhero=5Fhand=5Fpoint=20=E5=BD=92=E5=A3=B3?= =?UTF-8?q?=E5=B1=82=E5=9D=90=E6=A0=87=EF=BC=88fx=20=E5=9C=A8=E8=A7=86?= =?UTF-8?q?=E5=8F=A3=E5=A4=96=E9=A1=BB=E5=90=AB=E5=B1=85=E4=B8=AD=E5=81=8F?= =?UTF-8?q?=E7=A7=BB=EF=BC=89=EF=BC=9B=E2=91=A1B=20=E5=8C=BA=E7=AB=99?= =?UTF-8?q?=E6=A1=A9=E7=AB=8B=E7=BB=98=3D48=C3=9748=20hero=5Fidle=5F01=20?= =?UTF-8?q?=E6=8D=A2=E8=A3=85=EF=BC=88UiKit.TEX=5FHERO=5FPORTRAIT=EF=BC=8C?= =?UTF-8?q?=E6=BA=90=E6=A1=A3=20candidates/hero-idle/=EF=BC=8C=E7=B4=A0?= =?UTF-8?q?=E6=9D=90=E4=B8=8D=E5=85=A5=20git=EF=BC=89+set=5Fportrait=5Ftex?= =?UTF-8?q?ture=20=E5=A2=9E=20flip=20=E5=8F=82=E6=95=B0=EF=BC=88=E8=8B=B1?= =?UTF-8?q?=E9=9B=84=E7=AB=8B=E7=BB=98=E6=B0=B4=E5=B9=B3=E5=90=91=E5=8F=B3?= =?UTF-8?q?=EF=BC=89+=E7=9B=92=E9=AB=98=2096=3D48=20=E6=BA=90=C3=972=20?= =?UTF-8?q?=E6=95=B4=E6=95=B0=E6=A1=A3=EF=BC=88=E5=8E=9F=20106=20=E7=9B=92?= =?UTF-8?q?=E9=9D=9E=E6=95=B4=E6=95=B0=E7=BC=A9=E6=94=BE=E8=BF=9D=E5=8F=8D?= =?UTF-8?q?=E5=9C=A3=E7=BB=8F=20=C2=A78.2=EF=BC=89=EF=BC=9B=E2=91=A2ui=5Fi?= =?UTF-8?q?nput=5Fprobe=20=E6=96=B0=20case=E2=91=A6=20=E8=8B=B1=E9=9B=84?= =?UTF-8?q?=E8=90=BD=E4=BD=8D=E5=9B=9E=E5=BD=92=E9=97=B8=EF=BC=88=E5=A2=99?= =?UTF-8?q?=E9=92=9F=200.3s=20=E7=AD=89=20Tween=20=E5=88=B0=E4=BD=8D?= =?UTF-8?q?=E2=80=94=E2=80=94headless=20=E5=B8=A7=E6=95=B0=E7=AD=89?= =?UTF-8?q?=E5=BE=85<140ms=20=E4=BC=9A=E9=87=87=E5=8D=8A=E9=80=94=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E9=A6=96=E8=B7=91=E5=AE=9E=E8=AF=81=EF=BC=9B=E7=B4=A0?= =?UTF-8?q?=E6=9D=90=E7=BC=BA=E4=BD=8D=E6=A0=91=E8=B7=B3=E8=BF=87=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E5=85=AD=E6=96=AD=E8=A8=80=E2=86=92=E4=B8=83=E6=96=AD?= =?UTF-8?q?=E8=A8=80=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 --- game/presentation/views/battle_stage_view.gd | 2 +- game/presentation/views/board_view.gd | 12 +++++++----- game/presentation/views/standee_view.gd | 12 ++++++++---- game/presentation/views/ui_kit.gd | 1 + game/smoke/ui_input_probe.gd | 17 +++++++++++++++-- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/game/presentation/views/battle_stage_view.gd b/game/presentation/views/battle_stage_view.gd index b1c5a1f8..b31ce8f6 100644 --- a/game/presentation/views/battle_stage_view.gd +++ b/game/presentation/views/battle_stage_view.gd @@ -35,7 +35,7 @@ func _init(db_ref) -> void: vb.add_child(row) hero_standee = StandeeView.new("hero") - hero_standee.set_portrait_texture(UiKit.load_tex(UiKit.TEX_HERO_IDLE)) # 棋盘小人贴图批 2026-09-25(挂点现成——null 回退字形「人」;NEAREST 归 set_portrait_texture 单点) + hero_standee.set_portrait_texture(UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT), true) # B 区立绘 48×48 朝右(2026-09-26 用户点名 hero_idle_01 换装+水平向右;素材缺位 null 回退字形「人」) row.add_child(hero_standee) var center := CenterContainer.new() diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index 3f34c0c7..1b4dda3b 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -339,16 +339,18 @@ func cell_origin(c: Vector2i) -> Vector2: return _svp_container.position + Vector2(c) * (_cell + float(SEP)) -# 英雄 32×32 盒格内居中的脚底锚点(盒底中点——弹跳 scale.y 向此收敛) +# 英雄 32×32 盒格内居中的脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距, +# 禁带壳层居中偏移。2026-09-26 修正:上批误用壳层 cell_origin(含 _svp_container.position 居中 +# 偏移)定位世界内精灵,实测整体右偏 ~7 格+偏离格心——实机截图用户报缺陷) 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) + return Vector2(c) * (_cell + float(SEP)) + Vector2(m + HERO_FRAME_PX * 0.5, m + HERO_FRAME_PX) -# 英雄持握手位(fx 同层坐标系)——挥铲特效圆心(2026-09-25 用户裁决:以手为圆心、铲长为半径 -# 旋转;手 = 盒底往上 1/4 盒高、水平居中) +# 英雄持握手位(fx 同层坐标系 = 壳层坐标——fx 在 SubViewport 之外,须含 _svp_container.position +# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中(2026-09-25 用户裁决) func hero_hand_point() -> Vector2: - return _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) # 英雄精灵缩放档(fx 挥铲同档出口——两演出比例恒一致) diff --git a/game/presentation/views/standee_view.gd b/game/presentation/views/standee_view.gd index 3c37528c..c0115984 100644 --- a/game/presentation/views/standee_view.gd +++ b/game/presentation/views/standee_view.gd @@ -224,19 +224,23 @@ func _play_intent_motion() -> void: tw.tween_property(_portrait, "rotation_degrees", 0.0, UiKit.motion(0.14)) -# 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形 -func set_portrait_texture(tex: Texture2D) -> void: +# 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。 +# flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右——2026-09-26 用户点名英雄立绘水平向右)。 +# 盒高 96px = 48px 立绘源整数 2×(圣经 §8.2 禁非整数缩放;offset_top 44 = 血条+状态行之下净空)—— +# 源换代时随源重算盒高。 +func set_portrait_texture(tex: Texture2D, flip: bool = false) -> void: if _portrait_tex == null: _portrait_tex = TextureRect.new() _portrait_tex.expand_mode = TextureRect.EXPAND_IGNORE_SIZE _portrait_tex.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - _portrait_tex.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST # 32px 像素帧放大防糊(棋盘小人贴图批 2026-09-25) + _portrait_tex.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST # 像素帧放大防糊(棋盘小人贴图批 2026-09-25) _portrait_tex.set_anchors_preset(Control.PRESET_FULL_RECT) _portrait_tex.offset_top = 44.0 - _portrait_tex.offset_bottom = -26.0 + _portrait_tex.offset_bottom = -36.0 # 盒高 96 = 48 源 ×2 整数档(原 −26 盒高 106 系非整数缩放) _portrait_tex.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_portrait_tex) _portrait_tex.texture = tex + _portrait_tex.flip_h = flip _portrait_tex.visible = tex != null _portrait.visible = tex == null diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index 075ede83..b732d8f0 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -79,6 +79,7 @@ static func motion(dur: float) -> float: # 的字形/无演出回退路径;禁 preload(素材缺位树上直接断言炸脚本,冒烟安全红线) const TEX_HERO_IDLE := "res://assets/art/character/character_hero_idle.png" const TEX_HERO_WALK := "res://assets/art/character/character_hero_walk.png" +const TEX_HERO_PORTRAIT := "res://assets/art/character/character_hero_portrait.png" # B 区站桩立绘 48×48(源档 candidates/hero-idle/hero_idle_01.png,2026-09-26 用户点名换装) # 素材纹理守卫装载(exists 检查避开缺位树上 ResourceLoader 报错刷屏——冒烟/无素材检出静默回退) diff --git a/game/smoke/ui_input_probe.gd b/game/smoke/ui_input_probe.gd index 7f4817a3..790a74aa 100644 --- a/game/smoke/ui_input_probe.gd +++ b/game/smoke/ui_input_probe.gd @@ -4,7 +4,8 @@ # 本探针是真实输入路径的唯一自动化覆盖:鼠标事件 → 壳层 _gui_input → 板级公式换算(_cell_at)→ # 悬停/点选/chord 既有出口 → 组合根信号。四 case:①悬停可挖域(金框+十字光标)②悬停禁入 # (hover_forbidden 信号+灰禁红框)③悬停已开格(路径预览点迹+移动光标)④右键插旗(flag_place -# 到达组合根+核心旗态)⑤双击 chord(禁入格双左击,核心拒挖零副作用)⑥左键挖(dig 到达组合根+核心揭格)。 +# 到达组合根+核心旗态)⑤双击 chord(禁入格双左击,核心拒挖零副作用)⑥左键挖(dig 到达组合根+核心揭格) +# ⑦英雄精灵世界落位(棋盘小人贴图批回归闸——SubViewport 世界坐标判据,素材缺位树跳过)。 # 事件路径:Viewport.push_input(ev, in_local_coords=true)——落点用 BoardView.cell_center_local # (与 _cell_at 同一公式族)换算画布坐标,SubViewportContainer mouse_filter=IGNORE 不拦截。 # 运行:Godot_console.exe --headless --path . res://smoke/ui_input_probe.tscn @@ -120,8 +121,20 @@ func _ready() -> void: await get_tree().process_frame 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 时会采到半途位置(首跑实证),必须按墙钟等到位。 ---- + 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) + 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(): - print("[ui_input_probe] PASS — 悬停金框/禁入信号/路径预览/flag_place/chord/dig 六断言全绿(真实 push_input 路径)") + print("[ui_input_probe] PASS — 悬停金框/禁入信号/路径预览/flag_place/chord/dig/英雄落位 七断言全绿(真实 push_input 路径)") get_tree().quit(0) else: for p in _problems: