UE5.8 Common UI 喂饭级专题

UE5.8 Common UI 专题(三):Stack、Queue 和 UI Layer

把 CommonActivatableWidgetStack、Queue、RootContent、GeneratedWidgetsPool、Transition、Layer 组织和页面池化讲清楚,给出 RootLayout 的生产级结构。

总览

Common UI 的页面组织建议从 RootLayout 开始。不要让每个系统自己 AddToViewport(ZOrder)。ZOrder 用久了会变成“谁最后加谁在上面”,Back、焦点、ActionBar 和输入模式都不知道当前真正前台是谁。

UE5.8 Common UI 专题(三):Stack、Queue 和 UI Layer 配图
Stack 负责有返回路径的页面,Queue 负责一个一个来的消息;Layer 让 HUD、Menu、Modal、Toast 各守边界。

Stack 和 Queue 的差异

容器行为适合
UCommonActivatableWidgetStack栈顶显示/激活,栈顶停用后移除,前一个重新激活主菜单、设置页、背包、弹窗
UCommonActivatableWidgetQueue一次显示一个,当前停用后释放,下一个再显示Toast、奖励提示、排队确认
UCommonAnimatedSwitcher切换索引并播放动画Tab 内容区、步骤页
UCommonActivatableWidgetSwitcherSwitcher + Activatable 生命周期Tab 内容是 Activatable 页面时

Stack 表示“有返回路径”:A 推 B,B 返回 A。Queue 表示“按顺序消费”:弹窗 1 关了才显示弹窗 2。

RootLayout 推荐结构

WBP_RootLayout
  Overlay
    Layer_HUD        ordinary panel or activatable root
    Layer_Menu       CommonActivatableWidgetStack
    Layer_Modal      CommonActivatableWidgetStack
    Layer_Loading    CommonActivatableWidgetStack
    Layer_Toast      CommonActivatableWidgetQueue
    GlobalActionBar  CommonBoundActionBar

Modal 层建议在 Menu 层上方,因为确认框要阻断设置页。Toast 层通常不阻断输入,所以可以用 Queue 并让每个 Toast 不声明 Menu 输入配置。Loading 如果要阻塞输入,就用 Modal/Loading Stack 并让页面 bIsModal=true

AddWidget 的正确姿势

源码注释强烈推荐按类 AddWidget<T>(Class, InitFunc),不要手动创建实例再塞进去。原因是容器内部有 GeneratedWidgetsPool,Widget 可能被池化复用。

UInventoryScreen* Screen = LayerMenu->AddWidget<UInventoryScreen>(
    InventoryScreenClass,
    [InventoryId](UInventoryScreen& CreatedScreen)
    {
        CreatedScreen.SetupInventory(InventoryId);
    });

每次 Add 都要初始化页面所需数据,不要假设这个 Widget 是全新对象。

RootContent 的用法

UCommonActivatableWidgetStack 有 RootContent 概念:可以让栈底永远保留一个根页面。比如 MainMenu 作为根,设置页、存档页、画质页在上面 Push;所有上层关闭后回到 MainMenu,而 MainMenu 不会被普通 Pop 移除。

如果项目有“大厅 -> 多个子页”的结构,RootContent 很适合。游戏内暂停菜单也可以把 PauseHome 当根内容。

Transition 不要过度

容器有 TransitionTypeTransitionCurveTypeTransitionDuration 和 fallback strategy。初学建议先用 0.15 到 0.25 秒的轻微 Fade/Slide。不要每一层都做长转场,否则 Back 一次要等多个动画结束,输入延迟会被玩家感觉到。

使用案例:暂停菜单

打开暂停菜单时 Push WBP_PauseHome 到 Menu Stack。点设置 Push WBP_Settings。点音频 Push WBP_AudioSettings。按 Back:音频关闭回设置;再 Back 回 PauseHome;再 Back 关闭暂停菜单并恢复 gameplay input。

如果此时收到“队友邀请”Toast,放到 Toast Queue,不要 Push 到 Menu Stack,否则 Back 路径会被污染。

常见坑

  • Toast 用 Stack,玩家按 Back 把提示关掉还影响主菜单返回路径。
  • Modal 和 Menu 放同一个 Stack,确认框关闭后页面顺序混乱。
  • 缓存 AddWidget 返回的指针下次复用,结果拿到池化的旧页面。
  • RootLayout 每个地图创建一套但不清理,LocalPlayer 里出现多个根。
  • Loading 层只是视觉遮罩,没有声明 Modal/InputConfig,底下按钮仍能被点。

源码依据

UCommonActivatableWidgetContainerBase 注释说它管理 N 个 activatable widgets,一次显示一个,并且容器像黑盒,不暴露普通 Panel 那样的 child/slot 修改。源码还提醒 Widget 会被池化,不应缓存/重用创建出的 widget 状态。UCommonActivatableWidgetStack 说明栈顶停用后会被移除,前一个重新显示/激活;UCommonActivatableWidgetQueue 则是当前停用后释放并显示下一个。

源码路径索引

  • CommonUI/Public/Widgets/CommonActivatableWidgetContainer.h
  • CommonUI/Public/CommonAnimatedSwitcher.h
  • CommonUI/Public/CommonActivatableWidgetSwitcher.h