总览
第一篇只做一件事:让你在空项目里跑通一个“主菜单 -> 设置页 -> 返回主菜单”的闭环。先别急着做十层 UI,也别先调样式。Common UI 最怕的是第一步就绕过它的入口,后面所有输入路由都会歪。
第一步:启用插件和模块
编辑器里启用 Common UI Plugin。C++ 项目在 Build.cs 加:
PrivateDependencyModuleNames.AddRange(new[]
{
"CommonUI",
"CommonInput",
"EnhancedInput",
"UMG"
});
Common UI 依赖 Enhanced Input,所以项目里最好一起启用 Enhanced Input。UE5.8 源码里 CommonUI 插件默认不启用,说明它不是每个项目自动接管 UI 的功能,你必须明确把它接进项目架构。
第二步:配置 ViewportClient
在 DefaultEngine.ini 或 Project Settings 里设置:
[/Script/Engine.Engine]
GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient
UCommonGameViewportClient 会在 InputKey、InputAxis、InputTouch 里先把输入 reroute 给 UCommonUIActionRouterBase。没有这一步,Activatable Widget 能显示,但 Back/ActionBar/输入阻断会不稳定。
第三步:做 Common Input Data
创建一个蓝图类继承 CommonUIInputData,例如 BP_CommonInputData。配置:
DefaultClickAction:确认/点击动作。DefaultBackAction:返回动作。DefaultHoldData:长按默认时间。- 如果开启 Enhanced Input Support,再配置
EnhancedInputClickAction和EnhancedInputBackAction。
然后在 Project Settings > Common Input 里把 InputData 指向这个类。源码里 UCommonInputSettings::LoadData() 会加载这个类,按钮、返回键和 ActionBar 会从这里找默认动作。
第四步:创建 RootLayout
创建 WBP_RootLayout,继承普通 UserWidget 也可以,里面放几个命名清楚的层:
| 层 | 控件 | 用途 |
|---|---|---|
Layer_HUD | Overlay/Canvas | 血条、小地图、任务提示 |
Layer_Menu | CommonActivatableWidgetStack | 主菜单、设置、背包 |
Layer_Modal | CommonActivatableWidgetStack | 确认框、阻塞弹窗 |
Layer_Toast | CommonActivatableWidgetQueue | 奖励、提示、短弹窗 |
RootLayout 加到 Viewport 一次即可。后续页面都进 Stack/Queue,不要让各个页面自己 AddToViewport。
第五步:创建 MainMenu 和 SettingsScreen
WBP_MainMenu 和 WBP_SettingsScreen 继承 CommonActivatableWidget。MainMenu 里放“开始游戏”“设置”“退出”三个 CommonButton。SettingsScreen 里放一些选项和返回提示。
在 MainMenu 的设置按钮点击时:
Layer_Menu->AddWidget<UCommonActivatableWidget>(SettingsScreenClass);
蓝图里对应节点就是 CommonActivatableWidgetStack 的 Add Widget。源码里 Stack 的语义是:只显示并激活栈顶页面;栈顶 Deactivate 后会自动移除并重新显示/激活前一个页面。
第六步:配置返回键和焦点
在 WBP_SettingsScreen 上勾选:
Is Back HandlerIs Back Action Displayed In Action BarSupports Activation Focus
实现 BP_GetDesiredFocusTarget,返回页面里第一个应该聚焦的按钮。手柄项目这一步非常重要。没有 Desired Focus,页面打开后手柄导航可能看似“没反应”,其实焦点没落到可交互控件上。
第七步:放 ActionBar
创建 WBP_BoundActionButton,继承 CommonBoundActionButton,实现 CommonBoundActionButtonInterface 的默认需求,里面放一个 CommonActionWidget 和一个 CommonTextBlock,文本变量名按源码需要绑定为 Text_ActionName。
再创建一个 CommonBoundActionBar 放在 RootLayout 底部或每个页面底部,ActionButtonClass 指向 WBP_BoundActionButton。当 SettingsScreen 激活并注册 Back Action 后,ActionBar 会自动显示返回提示。
常见坑
- 忘记配置
CommonGameViewportClient,页面能显示但输入路由不完整。 - RootLayout 每次打开菜单都 Create 一次,导致多个 Action Router 根节点和焦点互相抢。
- 页面直接 AddToViewport,不进 Stack,Back 不知道该回到哪里。
- 没实现 Desired Focus,手柄打开菜单后没有初始焦点。
- 设置页关闭时手动 RemoveFromParent,而不是 DeactivateWidget,导致栈状态不一致。
源码依据
UCommonGameViewportClient 明确在输入进入游戏前调用 Action Router。UCommonActivatableWidgetStack 注释说明它是显示栈,栈顶 deactivation 后会被移除并激活前一个 widget。UCommonActivatableWidget 的 NativeConstruct 会在 bIsBackHandler 时注册默认 Back Action,Back 默认实现是 Deactivate 当前 Widget。
源码路径索引
CommonUI/Public/CommonGameViewportClient.hCommonUI/Public/Widgets/CommonActivatableWidgetContainer.hCommonInput/Public/CommonInputBaseTypes.hCommonUI/Public/CommonActionWidget.h