From 8604736aab7be662da46c27340518fe88cbbdc27 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sun, 20 Sep 2026 15:03:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(presentation)+docs:=20=E5=8F=8D=E9=A6=88?= =?UTF-8?q?=E4=B8=8E=E6=95=A3=E9=A1=B9=E5=BE=AE=E6=89=B9=E2=80=94=E2=80=94?= =?UTF-8?q?tooltip=20=E9=98=B2=E5=87=BA=E5=B1=8F/=E5=87=8F=E5=8A=A8?= =?UTF-8?q?=E6=95=88=E5=BC=80=E5=85=B3/HUD=20=E4=B9=89=E5=8A=A1=E6=B8=85?= =?UTF-8?q?=E5=8D=95=E8=A1=A5=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ① 状态槽 tooltip 防出屏(缺陷①家族回归):top_level 脱离容器布局,默认悬浮槽位上方, 越上缘自动翻转到槽位下方,再钳制视口内——原硬编码 position(0,-70) 边缘格裁切修复 ② 减动效承诺落地(battle.md §Transitions 内建承诺;HUD框架 验收行『时长减半或跳终态』): UiKit.motion_scale + UiKit.motion() 单点包装——新增动效禁写裸时长;本次包扎全部既有呈现层 tween(站桩意图/血条/闪白/死亡摇摆、结算光带/弹道、board_fx 全部演出、终局蜡封/旗光、 RAP/AP 脉冲、棋盘去饱和);可挖域呼吸减动效下停格(静态微光可辨);切片期入口 = TunePanel 『减动效』CheckButton,正式设置层归设置批(设置独立于存档两分制) ③ HUD框架 呈现义务来源清单补「关卡节点地图」行(节点链进度常显+选路覆盖层——漏登记补齐) + v0.7 修订记录 ④ 核对项(零改动):未生效行聚合计数已全覆盖——现行协议唯一未生效键 = reward_ineffective (满层/无存活敌人/满血等语义子型经 batch_key 区分,battle_log AGG_TYPES 无缺) ⑤ 验收:冒烟 355 项 0 失败 + ui_probe/tune_probe PASS + click_probe 问题=0;gdd_lint 基线 与 master 一致(8 个既有不通过,非本批引入) --- design/gdd/systems/表现与支撑/HUD框架.md | 2 ++ .../presentation/views/ap_bar_view.gd | 4 ++-- .../presentation/views/battle_end_overlay.gd | 10 ++++----- .../presentation/views/board_fx_view.gd | 22 +++++++++---------- .../presentation/views/board_view.gd | 4 +++- .../views/hero_info_panel_view.gd | 8 +++---- .../presentation/views/settlement_fx_view.gd | 12 +++++----- .../presentation/views/standee_view.gd | 20 ++++++++--------- .../presentation/views/status_slot_row.gd | 13 ++++++++++- .../presentation/views/tune_panel.gd | 8 +++++++ .../presentation/views/ui_kit.gd | 9 ++++++++ 11 files changed, 72 insertions(+), 40 deletions(-) diff --git a/design/gdd/systems/表现与支撑/HUD框架.md b/design/gdd/systems/表现与支撑/HUD框架.md index 1f2a984a..d6a99dc3 100644 --- a/design/gdd/systems/表现与支撑/HUD框架.md +++ b/design/gdd/systems/表现与支撑/HUD框架.md @@ -170,6 +170,7 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻 | [排雷玩法](../排雷循环/排雷玩法.md) | 已开 / 未开格态一眼可辨;悬停预承诺与可挖域常驻指示;移动路径预览即合法性指示;每次拒绝可感知且棋盘面细因可区分;停手被改判可感知 | | [行动经济](../排雷循环/行动经济.md) | 行动点与储备常显;储备清零反馈;棋盘面与资源面拒绝可区分;刷新入口与代价可读 | | [战斗结算](../战斗循环/战斗结算.md) | 未生效类明细行(奖励未生效)的呈现;高光行强调 | +| [关卡节点地图](../冒险循环/关卡节点地图.md) | 节点链进度常显(A 区顶栏:当前节点奖励金高亮、已过暗金、Boss 旗朱描边、候选下一跳亮金);选路覆盖层只读呈现(零自动推进停点语义) | | [敌人系统](../战斗循环/敌人系统.md) | 意图线索映射成文且全局恒定;单位顺位与血条可读;敌方共享层数各卡同值 | | [增益状态](../战斗循环/增益状态.md) | 状态栏常显;无状态时整排槽隐藏;有利 / 有害极性呈现不看数值符号 | | [地图战斗资源](../战斗循环/地图战斗资源.md) | 已开格的奖励身份常读 | @@ -249,3 +250,4 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻 | v0.3 | 2026-09-16 | 格面资源批 UI 欠账收口(可选类道具与其轮末窗口已随 2026-09-15 代码批删除):战斗屏布局五区 → 四区(删 E 道具栏行,D 区加宽至约 1036px 并同步装板校验变量);删道具窗口弹层条款(原位点亮、非模态不入栈)、P0 闭环窗口锚点条目、「道具窗口原位点亮 / 至多选一件 / 禁用件带原因 / 空库存不弹窗」验收行;依赖与输出表的窗口选择提交、道具使用窗口呈现、库存查询义务删除或改述为现行口径(奖励身份投影经 `query_identity` 只读回调,零直读);关卡末清库反馈义务改载魔法道具背包,随魔法道具 P0 落地。魔法道具预留义务行与「待拍板」登记保持 | | v0.5 | 2026-09-17 | 数值纪律收口批:调参表标注文档自持口径(无表承载常量的唯一事实源声明) | | v0.6 | 2026-09-20 | **敌人卡形态裁决收口批(与 battle.md / 战斗表现 / 06 清单同批,台账 99)**:全站桩形态拍板——B 区主角与敌人均为无框立绘+底座、状态小图标挂血条正下方,英雄卡/敌人卡退役;B 区行、敌人呈现义务行(载体由卡改立绘,意图占位/共享层数各立绘同值/死亡灰化退场等义务面平移)、下游战斗表现交付行、就地更新示例措辞四处同步;攻击值等规划数据仍归 C 区数据框(C 区不动)。呈现快照数据面零改动(消费字段不变,只换呈现载体) | +| v0.7 | 2026-09-20 | **表现层三批落地 + 义务清单补行(代码同日三批:样式收敛 / 站桩改造 / 结算演出链)**:呈现义务来源清单补「关卡节点地图」行(节点链进度常显 + 选路覆盖层只读呈现——此前漏登记);新增结算演出覆盖层(跨区弹道/光带载体,兑现演出因果可视);减动效承诺落地(UiKit.motion 单点包装全部呈现层 tween 时长,开启时长减半、可挖域呼吸停格;切片期入口 = TunePanel 开关,正式设置层归设置批)。代码侧站桩载体(StandeeView)与卡片退役随 v0.6 裁决执行 | diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd index 4da72546..409c042c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd @@ -171,8 +171,8 @@ func _disarm() -> void: # 资源面拒绝反馈(pattern 1 resource_face 腿):AP 数字脉冲 func pulse_ap() -> void: var tw := create_tween() - tw.tween_property(_ap_label, "modulate", Color(1.0, 0.4, 0.3), 0.1) - tw.tween_property(_ap_label, "modulate", Color(1, 1, 1), 0.25) + tw.tween_property(_ap_label, "modulate", Color(1.0, 0.4, 0.3), UiKit.motion(0.1)) + tw.tween_property(_ap_label, "modulate", Color(1, 1, 1), UiKit.motion(0.25)) func _make_dot() -> ColorRect: diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd index 836b9c91..9f07e1e6 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd @@ -99,8 +99,8 @@ func _play_seal_fall() -> void: _seal.position = Vector2(target.x, -100.0) _seal.visible = true var tw := _seal.create_tween() - tw.tween_property(_seal, "position:y", target.y - 26.0, 0.42).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) - tw.tween_property(_seal, "position:y", target.y, 0.22).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT) + tw.tween_property(_seal, "position:y", target.y - 26.0, UiKit.motion(0.42)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + tw.tween_property(_seal, "position:y", target.y, UiKit.motion(0.22)).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT) # 旗化光点收回(失败——插过的旗逐面化光点收回占位,§2.5) @@ -115,7 +115,7 @@ func _play_flag_recall(count: int) -> void: dot.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(dot) var tw := dot.create_tween() - tw.tween_interval(0.1 * i) - tw.tween_property(dot, "position:y", dot.position.y - 120.0, 0.8) - tw.parallel().tween_property(dot, "modulate:a", 0.0, 0.8) + tw.tween_interval(UiKit.motion(0.1 * i)) + 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) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd index f9bd3dae..990082ef 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd @@ -32,7 +32,7 @@ func play_clear_burst() -> void: var fountain := CPUParticles2D.new() fountain.position = size * 0.5 fountain.amount = 90 - fountain.lifetime = 1.6 + fountain.lifetime = UiKit.motion(1.6) fountain.one_shot = true fountain.explosiveness = 0.2 fountain.direction = Vector2(0, -1) @@ -48,7 +48,7 @@ func play_clear_burst() -> void: var confetti := CPUParticles2D.new() confetti.position = Vector2(size.x * 0.5, -8.0) confetti.amount = 60 - confetti.lifetime = 3.2 + confetti.lifetime = UiKit.motion(3.2) confetti.one_shot = true confetti.explosiveness = 0.9 confetti.direction = Vector2(0, 1) @@ -62,7 +62,7 @@ func play_clear_burst() -> void: confetti.emitting = true add_child(confetti) var tw := create_tween() - tw.tween_interval(3.5) + tw.tween_interval(UiKit.motion(3.5)) tw.tween_callback(func(): for n in [fountain, confetti]: if is_instance_valid(n): @@ -85,8 +85,8 @@ func play_boss_shadow() -> void: shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(shadow) var tw := shadow.create_tween() - tw.tween_property(shadow, "position:y", size.y, 1.1).set_trans(Tween.TRANS_SINE) - tw.tween_property(shadow, "modulate:a", 0.0, 0.5) + tw.tween_property(shadow, "position:y", size.y, UiKit.motion(1.1)).set_trans(Tween.TRANS_SINE) + tw.tween_property(shadow, "modulate:a", 0.0, UiKit.motion(0.5)) tw.tween_callback(shadow.queue_free) @@ -100,7 +100,7 @@ func play_perfect_sweep() -> void: band.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(band) var tw := band.create_tween() - tw.tween_property(band, "position:x", size.x + 8.0, 0.5 + 0.15 * i) + tw.tween_property(band, "position:x", size.x + 8.0, UiKit.motion(0.5 + 0.15 * i)) tw.tween_callback(band.queue_free) _show_banner("完美排雷 ×2") @@ -115,8 +115,8 @@ func play_flag_hit_projectile(heavy: bool) -> void: shot.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(shot) var tw := shot.create_tween() - tw.tween_property(shot, "position", Vector2(size.x * 0.75, -30.0), 0.35) - tw.parallel().tween_property(shot, "modulate:a", 0.0, 0.35) + tw.tween_property(shot, "position", Vector2(size.x * 0.75, -30.0), UiKit.motion(0.35)) + tw.parallel().tween_property(shot, "modulate:a", 0.0, UiKit.motion(0.35)) tw.tween_callback(shot.queue_free) @@ -125,7 +125,7 @@ func _show_banner(text: String) -> void: _banner.visible = true _banner.modulate.a = 0.0 var tw := create_tween() - tw.tween_property(_banner, "modulate:a", 1.0, 0.2) - tw.tween_interval(1.6) - tw.tween_property(_banner, "modulate:a", 0.0, 0.4) + tw.tween_property(_banner, "modulate:a", 1.0, UiKit.motion(0.2)) + tw.tween_interval(UiKit.motion(1.6)) + tw.tween_property(_banner, "modulate:a", 0.0, UiKit.motion(0.4)) tw.tween_callback(func(): _banner.visible = false) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd index 5c175f76..72cb6f36 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd @@ -84,7 +84,7 @@ func set_desaturated(on: bool) -> void: _desat_tween = create_tween() var target := 1.0 if on else 0.0 _desat_tween.tween_method(func(v: float): _desat_mat.set_shader_parameter("strength", v), - float(_desat_mat.get_shader_parameter("strength")), target, 0.15) + float(_desat_mat.get_shader_parameter("strength")), target, UiKit.motion(0.15)) for c in _cells: (_cells[c] as Button).material = _desat_mat if on else null @@ -372,6 +372,8 @@ func _process(delta: float) -> void: if not _dig_glow or _diggable.is_empty(): return _breath_t += delta + if UiKit.motion_scale < 1.0: + return # 减动效:呼吸停格(保留当前微光边框——静态可辨、无持续运动) var k := 0.5 + 0.5 * sin(_breath_t * 3.0) # 3 rad/s≈0.48Hz——无 >3 次/秒频闪(AC 基线) var col := COL_DIG_EDGE.lerp(UiKit.DIG_EDGE_DEEP, 0.55 - 0.35 * k) for c in _diggable: diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd index 4082063b..aa6379cb 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd @@ -97,8 +97,8 @@ func set_rap(rap: int, cap_hint: int) -> void: # battle_over RAP 清零可见反馈(行动经济 D1 义务):数字滚动归零 + 微文案占位 _rap_feedback.text = "随战斗结束消散" var tw := create_tween() - tw.tween_property(_rap_label, "modulate", Color(1.4, 1.2, 0.7), 0.12) - tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), 0.3) + tw.tween_property(_rap_label, "modulate", Color(1.4, 1.2, 0.7), UiKit.motion(0.12)) + tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), UiKit.motion(0.3)) elif rap > 0: _rap_feedback.text = "" @@ -135,8 +135,8 @@ func rap_text() -> String: # 资源面拒绝反馈(pattern 1 resource_face 腿):RAP 数字脉冲 func pulse_rap() -> void: var tw := create_tween() - tw.tween_property(_rap_label, "modulate", Color(1.0, 0.4, 0.3), 0.1) - tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), 0.25) + tw.tween_property(_rap_label, "modulate", Color(1.0, 0.4, 0.3), UiKit.motion(0.1)) + tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), UiKit.motion(0.25)) func _render_rap_dots(rap: int) -> void: diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_fx_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_fx_view.gd index 5c13b10b..acd09a97 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_fx_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_fx_view.gd @@ -28,8 +28,8 @@ func play_round_sweep(board_rect: Rect2) -> void: band.size = Vector2(board_rect.size.x, 34.0) add_child(band) var tw := band.create_tween() - tw.tween_property(band, "position:y", board_rect.end.y - 34.0, 0.5).set_trans(Tween.TRANS_SINE) - tw.parallel().tween_property(band, "modulate:a", 0.0, 0.5).set_delay(0.15) + 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) @@ -49,8 +49,8 @@ func play_projectile(from: Vector2, to: Vector2, glyph: String, col: Color, crit var mid := (from + to) * 0.5 + Vector2(0, -36.0) # 轻微上抛弧线 var tw := shot.create_tween() if crit: - tw.tween_property(shot, "scale", Vector2(1.5, 1.5), 0.08) - tw.tween_property(shot, "position", mid, 0.16).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) - tw.tween_property(shot, "position", to - shot.size * 0.5, 0.19).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN) - tw.parallel().tween_property(shot, "modulate:a", 0.0, 0.12).set_delay(0.23) + tw.tween_property(shot, "scale", Vector2(1.5, 1.5), UiKit.motion(0.08)) + tw.tween_property(shot, "position", mid, UiKit.motion(0.16)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) + tw.tween_property(shot, "position", to - shot.size * 0.5, UiKit.motion(0.19)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN) + tw.parallel().tween_property(shot, "modulate:a", 0.0, UiKit.motion(0.12)).set_delay(UiKit.motion(0.23)) tw.tween_callback(shot.queue_free) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/standee_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/standee_view.gd index 93097766..e921dde7 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/standee_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/standee_view.gd @@ -143,7 +143,7 @@ func _set_hp(hp: int) -> void: if _hp_tween != null and _hp_tween.is_valid(): _hp_tween.kill() _hp_tween = create_tween() - _hp_tween.tween_property(_hp_bar, "value", target, 0.15) + _hp_tween.tween_property(_hp_bar, "value", target, UiKit.motion(0.15)) func set_idle() -> void: @@ -203,10 +203,10 @@ func _play_intent_motion() -> void: _intent_badge.pivot_offset = _intent_badge.size * 0.5 _portrait.pivot_offset = _portrait.size * 0.5 var tw := create_tween() - tw.tween_property(_intent_badge, "scale", Vector2(1.6, 1.6), 0.10) - tw.tween_property(_intent_badge, "scale", Vector2.ONE, 0.16) - tw.parallel().tween_property(_portrait, "rotation_degrees", 4.0, 0.10) - tw.tween_property(_portrait, "rotation_degrees", 0.0, 0.14) + tw.tween_property(_intent_badge, "scale", Vector2(1.6, 1.6), UiKit.motion(0.10)) + tw.tween_property(_intent_badge, "scale", Vector2.ONE, UiKit.motion(0.16)) + tw.parallel().tween_property(_portrait, "rotation_degrees", 4.0, UiKit.motion(0.10)) + tw.tween_property(_portrait, "rotation_degrees", 0.0, UiKit.motion(0.14)) # 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形 @@ -231,9 +231,9 @@ func mark_dead() -> void: _dead = true modulate = Color(0.62, 0.62, 0.62, 0.9) var tw := create_tween() - tw.tween_property(self, "rotation_degrees", 2.5, 0.12) - tw.tween_property(self, "rotation_degrees", -1.5, 0.1) - tw.tween_property(self, "rotation_degrees", 0.0, 0.08) + 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)) _refresh_portrait_glyph() if _intent_badge != null: _intent_badge.visible = false @@ -247,8 +247,8 @@ func is_dead() -> bool: # 受击闪白(结算演出占位——§2.2 受击反馈) func flash_hit() -> void: var tw := create_tween() - tw.tween_property(self, "modulate", Color(1.6, 1.4, 1.2), 0.08) - tw.tween_property(self, "modulate", Color(1, 1, 1) if not _dead else Color(0.62, 0.62, 0.62, 0.9), 0.2) + tw.tween_property(self, "modulate", Color(1.6, 1.4, 1.2), UiKit.motion(0.08)) + tw.tween_property(self, "modulate", Color(1, 1, 1) if not _dead else Color(0.62, 0.62, 0.62, 0.9), UiKit.motion(0.2)) func _revive() -> void: diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd index fefe0490..11d22ea0 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd @@ -144,7 +144,18 @@ func _on_slot_hover(slot: Control) -> void: int(eff.get("remaining_settlements", 0)), src, int(eff.get("round_index", 0))] if not _tip.is_inside_tree(): add_child(_tip) - _tip.position = Vector2(0, -70) + _tip.reset_size() + # 防出屏定位(缺陷①家族回归——边缘格 tooltip 硬编码上偏移会被视口裁切): + # 默认悬浮槽位上方,越上缘翻转到槽位下方,再钳制在视口内;top_level 脱离容器布局管理 + _tip.top_level = true + var slot_rect := slot.get_global_rect() + var desired := slot_rect.position + Vector2(0.0, -_tip.size.y - 6.0) + var vp := get_viewport_rect().size + desired.x = clampf(desired.x, 4.0, maxf(4.0, vp.x - _tip.size.x - 4.0)) + if desired.y < 4.0: + desired.y = slot_rect.end.y + 6.0 + desired.y = clampf(desired.y, 4.0, maxf(4.0, vp.y - _tip.size.y - 4.0)) + _tip.global_position = desired _tip.visible = true diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd index e9283b77..3590cb0f 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd @@ -159,6 +159,14 @@ func _build() -> void: var reset_btn := UiKit.button("重置为 0") reset_btn.pressed.connect(_reset_btn_pressed) btn_row.add_child(reset_btn) + # 减动效开关(battle.md §Transitions 内建承诺——切片期入口;正式设置层归设置批,两分制) + var motion_row := HBoxContainer.new() + box.add_child(motion_row) + motion_row.add_child(UiKit.label("减动效(动画时长减半——运动敏感/演示用)", 14, false)) + var motion_btn := CheckButton.new() + motion_btn.button_pressed = UiKit.motion_scale < 1.0 + motion_btn.toggled.connect(func(on: bool): UiKit.motion_scale = 0.5 if on else 1.0) + motion_row.add_child(motion_btn) box.add_child(HSeparator.new()) var sim_row := HBoxContainer.new() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd index 12411c3d..d5ca5be1 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd @@ -65,6 +65,15 @@ const HEX_ON_GOLD := "3a2a08" const DOT_OFF := PANEL_BORDER +# ── 减动效(battle.md §Transitions 内建承诺:开启时时长减半;结算逐行兑现为信息载体降速不跳过)── +# 切片期入口 = TunePanel 开关(设置独立于存档两分制——正式设置层载体随设置批落位) +static var motion_scale := 1.0 # 1.0 常态 / 0.5 减动效 + +# 全部呈现层 tween 时长经本函数包装——新增动效一律 UiKit.motion(秒),禁再写裸时长字面量 +static func motion(dur: float) -> float: + return dur * motion_scale + + # ── StyleBox 工厂 ── # margin = (左, 上, 右, 下);border_w = 0 即无边框(根底/遮罩用) static func panel_flat(bg: Color, border: Color, corner: int = 0, border_w: int = 1, margin: Vector4 = Vector4.ZERO) -> StyleBoxFlat: