跳到主要内容

UI系统概述

OpenMon的UI层位于 Assets/OpenMon/Core/Runtime/UI/,完全基于Unity的UGUI(Canvas + RectTransform)构建。每个屏幕、菜单和对话框都共享两个基础构建块:MenuSelector 用于可导航的列表/网格,以及 DialogManager 用于模态文本框、选项和复杂弹出窗口。

架构一览​

UI/
├── MenuSelector.cs # 所有可导航菜单的基类
├── ScrollableMenuSelector.cs # MenuSelector + ScrollRect支持
├── VirtualizedMenuSelector.cs # MenuSelector + 回收/池化项
├── MenuItem.cs # MenuSelector中的单个按钮
├── VirtualizedMenuItem.cs # 感知其在池中行的MenuItem
├── UIElementsInstaller.cs # UI工厂的Zenject安装器
├── Battle/ # 战斗HUD:主菜单、招式菜单、目标选择器
├── Bags/ # 带标签页的背包/物品栏画面
├── Dex/ # 带标签页的完整图鉴风格画面
├── Dialogs/ # DialogManager + 选项菜单、文本输入、弹窗
├── GameMenu/ # 游戏内暂停菜单(图鉴、宝可梦、背包、地图等)
├── MainMenu/ # 标题画面:新游戏、继续、制作人员
├── Monsters/ # 宝可梦列表画面、摘要、存储
├── Options/ # 设置画面(音量、分辨率、语言等)
├── Shops/ # 买卖商店界面
├── Quests/ # 任务日志画面
├── Profile/ # 玩家资料 / 训练家卡
├── Map/ # 世界地图叠加层
└── ...

两大支柱​

MenuSelector 是几乎所有交互式菜单继承的基类。它处理:

  • 垂直或水平导航 — 通过 Navigation 枚举。
  • 选择器箭头 — 通过 MCE 内置的补间 API 动画移到悬停项。
  • 音频反馈 — 导航和选择时。
  • 输入集成 — 通过 IInputReceiver 接口和 IInputManager。
  • 循环导航 — 从最后一项继续导航会回到第一项,反之亦然。
  • 长按滚动 — 当 CanHoldToScrollFaster 启用时,按住输入会以可配置的间隔重复导航。

有两种专门的变体:

类名用例
ScrollableMenuSelector添加 ScrollRect,自动滚动以保持选中项可见。
VirtualizedMenuSelector<TData, TButton, TFactory>大数据集(例如图鉴、存储)的对象池列表。随着玩家滚动回收按钮实例。

详见菜单页面深入了解 MenuSelector。

DialogManager — 模态对话框和复杂弹窗​

DialogManager 是一个 单例,拥有游戏中所有模态UI覆盖层。它管理:

  • 基本文本对话框 — 带有打字机动画、说话者姓名面板和本地化。
  • 选项菜单 — 是/否,最多11个选项。
  • 全屏画面 — 通过静态方法访问:图鉴、背包、阵容、商店、选项、资料、任务、地图。
  • 专业化弹窗 — XP获取面板、招式替换对话框、招式传授、新宝可梦弹窗、文本输入对话框。
  • 通知 — 通过子 NotificationManager。

DialogManager通过Zenject的工厂模式实例化:

// DialogManagerInstaller.cs
Container.BindFactory<DialogManager, DialogManager.Factory>()
.FromComponentInNewPrefab(Prefab);

详见对话框页面了解完整API。

依赖注入​

所有UI工厂通过两个Zenject ScriptableObjectInstaller 注册:

  • DialogManagerInstaller — 从预制件绑定 DialogManager.Factory。
  • UIElementsInstaller — 绑定物品按钮、宝可梦按钮、图鉴按钮、招式按钮、任务按钮、存档按钮等的工厂。每个绑定通过 .WhenInjectedInto<T>() 限定作用域,以便每个屏幕使用正确的预制件变体。
// UIElementsInstaller示例
Container.BindFactory<ItemButton, ItemButton.Factory>()
.FromComponentInNewPrefab(BagItemButtonPrefab)
.AsCached()
.WhenInjectedInto<BagTab>()
.Lazy();

专用屏幕控制器​

每个主屏幕扩展 MenuSelector(用于网格/列表导航)、VirtualizedMenuSelector(用于大型池化列表)或 HidableUiElement(用于自定义布局)。所有屏幕都通过 DialogManager 启动:

屏幕控制器基类通过以下启动
游戏菜单GameMenuScreenMenuSelectorDialogManager.ShowGameMenu(...)
宝可梦列表MonstersMenuScreenHidableUiElementDialogManager.ShowPlayerRosterMenu(...)
背包 / 物品栏BagScreenHidableUiElementDialogManager.ShowBag(...)
图鉴DexScreenVirtualizedMenuSelectorDialogManager.ShowDexScreen(...)
单宝可梦图鉴SingleMonsterDexScreenHidableUiElementDialogManager.ShowSingleMonsterDexScreen(...)
商店ShopDialogHidableUiElementDialogManager.ShowShop(...)
选项OptionsScreenMenuSelectorDialogManager.ShowOptionsMenu()
资料ProfileScreenHidableUiElementDialogManager.ShowProfileScreen(...)
任务QuestsScreenVirtualizedMenuSelectorDialogManager.ShowQuestsScreen(...)
文本输入TextInputDialogHidableUiElementDialogManager.RequestTextInput(...)
招式传授MoveTutorDialogHidableUiElementDialogManager.ShowMoveTutorDialog(...)

战斗UI​

战斗HUD有自己的一套 MenuSelector 子类,不由DialogManager管理:

控制器基类用途
MainBattleMenuMenuSelector战斗 / 背包 / 宝可梦 / 逃跑 选择
BattleMoveMenuSelectorMenuSelector战斗中的招式选择
TargetMonstersMenuSelectorMenuSelector多战斗中的目标选择
BattleMonstersMenuHidableUiElement战斗中切换宝可梦
BattleInfoPanelHidableUiElement<MoveInfoPanel>详细战斗状态覆盖层
LastBallMenuMenuSelector快速投掷上次使用的球

输入系统集成​

所有菜单实现 IInputReceiver,它扩展了Unity新输入系统的操作接口:

public interface IInputReceiver : MCEInputActions.IMainActions,
MCEInputActions.IUIActions,
MCEInputActions.ITextInputActions
{
InputType GetInputType();
void OnStateEnter();
void OnStateExit();
string GetDebugName();
}

IInputManager 维护一个接收器的栈。当菜单调用 RequestInput() 时,它会将自己推入栈中并成为活动接收器。调用 ReleaseInput() 会将其弹出。这自然地处理了嵌套菜单——从游戏菜单中打开背包会将背包的输入接收器推到顶部,关闭时恢复游戏菜单的输入。

自定义UI屏幕的最佳实践​

  1. 继承 MenuSelector(大型列表使用 ScrollableMenuSelector / VirtualizedMenuSelector)。
  2. 为菜单中的每个按钮添加 MenuItem 组件。设置选择器箭头的 ArrowSelectorPosition 变换。
  3. 如果您的屏幕使用池化/虚拟化项目,在 UIElementsInstaller 中注册工厂。
  4. 按照现有模式添加静态方法和序列化引用,从 DialogManager 启动。
  5. 对所有检视器引用使用 [SerializeField] private — 除非字段是API的一部分,否则绝不将它们公开为 public。
  6. 对所有玩家可见的文本使用本地化键。LocalizedTextMeshPro 和 LocalizedTypeWriterTMP 组件会自动处理。
  7. 新编辑器工具优先使用UI Toolkit,但运行时游戏UI坚持使用UGUI以保持与现有系统的一致性。