vampire-like/docs/UIModule-使用说明.md

6.6 KiB
Raw Blame History

UIModule 使用说明(可选插件)

1. 定位

UIModule 是基于 UGF 的可选 UI 插件,通过 .asmref 融入现有项目程序集:

  • Base/SepCore.Base
  • Runtime/SepCore.Runtime
  • UI/SepCore.Presentation

插件本身没有独立运行时程序集基础抽象、事件、Controller、View 会并入宿主程序集编译。
不启用插件时,项目仍可继续使用原有 UI 路线。


2. 接入步骤

  1. 确保以下 .asmref 已存在于插件目录:
    • SepCore.Base.UIModule.asmref
    • SepCore.Runtime.UIModule.asmref
    • SepCore.Presentation.UIModule.asmref
  2. Assets/Plugins/UIModule/Prefab/DialogForm.prefab 复制到:
    • Assets/GameMain/UI/UIForms
  3. 处理原有 Dialog 资源(任选其一):
    • 重命名旧 DialogForm 资源,或
    • 确认无引用后删除旧 DialogForm 资源
  4. Launcher 场景的 Customs 下创建对象并挂载 UIRouterComponent

3. UIRouter 配置

UIRouterComponent 通过 Inspector 配置 UIFormType -> Controller 映射:

  • UI Form Type
  • Controller Type

编辑器会扫描所有实现 IUIFormController可实例化类型(非抽象、非接口、非开放泛型)。
运行时 Awake() 会自动注册配置项。

当前 Router 对外接口:

  • BindUIUseCase(UIFormType uiFormType, IUIUseCase useCase)
  • OpenUIAsync(UIFormType uiFormType, object userData = null, float timeout = 30f)
  • CloseUIAsync(UIFormType uiFormType, object userData = null, float timeout = 30f)

说明:

  • 缺少 Controller 绑定时会输出 Error 并直接失败
  • 不会 fallback 到原始 GameEntry.UI.OpenUIForm(...)
  • 重新注册同一 UIFormType 时,旧 Controller 会异步关闭并被新配置覆盖

4. 当前运行时结构

4.1 基础抽象

IUIFormController 当前是 async-first 接口:

UniTask<int?> OpenUIAsync(object userData = null, float timeout = 30f);
UniTask CloseUIAsync(object userData = null, float timeout = 30f);
void BindUseCase(IUIUseCase useCase);

UIFormControllerBase<TContext, TForm> 当前只提供薄机制层能力:

  • OpenFormAsync(TContext context, float timeout = 30f)
  • CloseFormAsync(object userData = null, float timeout = 30f)

它负责:

  • 调用 GameEntry.UI.OpenUIFormAsync(...) / CloseUIFormAsync(...)
  • 维护 _context / _form / _formSerialId
  • 在打开后调用 RefreshUI
  • 在打开 / 关闭时订阅与解除 SubscribeCustomEvents()

不负责

  • 解析 userData
  • 构造 Context
  • 缓存业务回调
  • 安排子类的业务时序

这些流程由具体 Controller 显式实现。

4.2 UGF 异步适配

插件的 async 行为并没有改 UGF 底层事件机制,而是通过以下适配层桥接:

  • AsyncTaskHelper
  • UIAsyncExtension

其中 UIAsyncExtension 目前已支持:

  • OpenUIFormAsync(string uiFormAssetName, string uiGroupName, ...)
  • OpenUIFormAsync(int uiFormId, object userData = null, float timeout = 30f)
  • CloseUIFormAsync(int serialId, object userData = null, float timeout = 30f)

5. Controller 实现约定

当前推荐写法是:

  1. 子类自己在 OpenUIAsync(...) 里:
    • 校验 userData
    • 构造 RawData / Context
    • 缓存本次交互需要的临时状态
    • await OpenFormAsync(...)
  2. 子类自己在 CloseUIAsync(...) 里:
    • 清理本次交互缓存
    • await CloseFormAsync(...)
  3. 基类只提供共用开关窗机制,不隐藏业务时序

也就是说,实现 Controller 时,应该尽量做到:

  • 看子类本身就能看懂完整流程
  • 不依赖“阅读基类钩子调用顺序”来推断业务时机

6. Dialog 当前实现

6.1 相关类型

  • DialogRawDataSepCore.UI

    • 外部输入模型
    • 可携带:
      • 标题 / 内容 / 按钮文本
      • PauseGame
      • OnClickConfirm / OnClickCancel / OnClickOther
      • UserData
  • DialogContextSepCore.UIModule

    • 纯展示数据
    • 不携带回调
  • DialogControllerSepCore.UIModule

    • 显式实现 OpenUIAsync / CloseUIAsync
    • 负责缓存按钮回调与 UserData
    • 负责把 DialogRawData 转成 DialogContext
  • DialogFormSepCore.UIModule

    • 只负责渲染和派发 UI 专用事件
  • DialogEventArgsSepCore.Event

    • 当前 Dialog 只使用一个事件类型
    • 通过 ButtonId 区分按钮:
      • 1Confirm
      • 2Cancel
      • 3Other

6.2 当前数据流

  1. 外部调用:

    await uiRouter.OpenUIAsync(UIFormType.DialogForm, rawData);
    
  2. DialogController.OpenUIAsync(...)

    • 校验 DialogRawData
    • 构造 DialogContext
    • 缓存按钮回调和 UserData
    • await OpenFormAsync(context, timeout)
  3. DialogForm 在按钮点击时只发出:

    • DialogEventArgs.Create(1, null)
    • DialogEventArgs.Create(2, null)
    • DialogEventArgs.Create(3, null)
  4. DialogController 收到 DialogEventArgs 后根据 ButtonId 选出对应回调

  5. DialogController 调用异步关闭流程,再执行回调:

    • await CloseUIAsync()
    • callback?.Invoke(userData)
  6. CloseUIAsync() 会先清理缓存,再交给基类关闭 UI

6.3 当前限制

  • DialogController.OpenUIAsync(null) 会失败并输出 warning
  • Dialog 当前不依赖 UseCase
  • DialogForm 当前没有本地化默认文本回退:
    • 标题、正文、按钮文本都以 DialogRawData 传入值为准

7. 注意点

  1. 先注册再打开
    使用前需确保 UIRouterComponent 已完成 Awake 自动注册。

  2. Controller 必须绑定且可实例化
    缺少绑定时 Router 直接失败,不会回退到原生打开路径。

  3. 统一走 async 接口
    启用 UIModule 管理的 UI应统一通过

    • OpenUIAsync(...)
    • CloseUIAsync(...)
  4. Controller 负责显式业务流程
    基类只提供开关窗机制;userData 校验、Context 构造、回调缓存与关闭后的后续动作都应在具体 Controller 中明确写出。

  5. 保持依赖方向

    • SepCore.BaseUI 专用事件
    • SepCore.RuntimeUI 基础抽象、RawDataUseCaseUIRouterComponent
    • SepCore.PresentationController、Context、View
  6. Prefab 脚本一致性
    Dialog prefab 上必须挂载插件内 SepCore.UIModule.DialogForm

  7. Dialog 事件语义
    当前 Dialog 使用单一 DialogEventArgs + ButtonId 区分按钮,不再使用多个按钮事件类型。

  8. Dialog 当前按单实例思路使用
    DialogController 当前没有额外用 sender / serialId 做多实例过滤,默认按单实例 Dialog 使用。