3.2 UMG HUD 与伤害飘字:数据驱动 UI

核心结论

HUD 与飘字是反馈层里最"晚到"的(50~200ms 那一档),但工程上最容易乱:飘字要随机抖动、要跟随 3D 位置、要合并同源伤害……做法的核心是数据驱动 + 独立的 Widget 池,把表现和逻辑彻底分家(UMG 最佳实践 https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/umg-best-practices-in-unreal-e-1da5 讲设计原则,我讲落地)。

HUD 的组件分层

W_HUD_Root(Canvas)
+-- W_HUD_SafeLayer     -- 永远不被遮挡的玩家状态(血条/体力)
+-- W_HUD_Gameplay      -- gameplay 相关(小地图、刷怪计数)
+-- W_FloatingLayer     -- 飘字/boos 提示(3D 投影到屏幕)
+-- W_HUD_Menu          -- 菜单(开关)

规则:HUD 不写任何玩法逻辑,它只订阅事件、显示数据。血条变化 = 订阅 OnHealthChanged,连"什么时候低血量变红"都写在 Widget 的样式逻辑里。

伤害飘字的工程化

飘字看着简单,做好要处理五个问题:

问题 我的做法
位置 受击者骨骼位置 Project 到屏幕,再加随机偏移
合并 同一目标 100ms 内的伤害合并显示("x3"或累加)
暴击 大字号 + 金色 + 特殊曲线
卡顿 对象池预生成 30 个飘字 Widget
边界 目标在屏幕外时贴边显示(指引方向)

飘字的 UI 池实现

W_FloatingLayer (CanvasPanel)
  生成时:预创建 20~30 个 W_DamageNumber(隐藏)
  触发:FindHidden -> Fill Data -> PlayFloatAnim
  动画完:Hide(交还池)

关键:飘字每帧重算目标屏幕位置(如果跟随 3D 目标),所以动画用偏移动画而不是绝对位置动画(绝对位置在目标移动时会跳)。

HUD 数据流(数据驱动)

[ASC 属性变化] 
    -> OnHealthChanged 事件
    -> PlayerController 广播 HUD Update
    -> W_HUD_SafeLayer 的 HealthBar -> 设置百分比与颜色

不要:Widget 里 Cast 到角色读 Attribute(联机时客户端数据时机错)。要:OnAttributeChanged 广播到 UI(GAS 的标准做法)。

常见坑与解法

坑 1:HUD 也在暂停时被冻结

原因:HUD 的动画用同一个时间轴。

解法:暂停时把 HUD 动画设 Pause 或给飘字 Widget 用 GameViewportClient 的独立计时(常见做法:暂停时飘字立即结算消失,不再播新飘字)。

坑 2:飘字数量爆炸(群攻时满屏数字)

解法: - 合并(100ms 内同目标); - 同屏上限(最多 12 个,多的直接丢弃或合并); - 群攻的 AOE 伤害只显示"总伤害"大字 + 目标身上小飘字。

坑 3:UI 挡住操作

解法:所有纯表现 Widget 的 Visibility 设 Hit Test Invisible(不是 Self Hit Test)。能点的才接受点击。

坑 4:性能:HUD Tick 每帧刷数据

解法:事件驱动——血条只在变化时更新;小地图用低频 Tick(5~10Hz)+ 插值动画平滑。UMG 的 Tick 是游戏线程的债。

相关节点速查

  • UMG 最佳实践:https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/umg-best-practices-in-unreal-e-1da5
  • HUD 组织:https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/user-interfaces-and-huds-in-un-e50b
  • 3D->屏幕投影:ProjectWorldToScreen
  • 暴击判定:伤害 GE 里带 Tag(Crit),UI 看 Tag 选样式

心得:飘字是玩家"看得见的数值",我花在它上的时间比血条多三倍。调好合并与对象池后,群攻打 boss 时数字清晰、不卡——细节反馈做到位,玩家会说"这游戏打击感好",虽然他说不出为什么。