更新文档
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
# 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** 接口:
|
||||
|
||||
```csharp
|
||||
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 相关类型
|
||||
|
||||
- `DialogRawData`(`SepCore.UI`)
|
||||
- 外部输入模型
|
||||
- 可携带:
|
||||
- 标题 / 内容 / 按钮文本
|
||||
- `PauseGame`
|
||||
- `OnClickConfirm / OnClickCancel / OnClickOther`
|
||||
- `UserData`
|
||||
|
||||
- `DialogContext`(`SepCore.UIModule`)
|
||||
- 纯展示数据
|
||||
- 不携带回调
|
||||
|
||||
- `DialogController`(`SepCore.UIModule`)
|
||||
- 显式实现 `OpenUIAsync / CloseUIAsync`
|
||||
- 负责缓存按钮回调与 `UserData`
|
||||
- 负责把 `DialogRawData` 转成 `DialogContext`
|
||||
|
||||
- `DialogForm`(`SepCore.UIModule`)
|
||||
- 只负责渲染和派发 UI 专用事件
|
||||
|
||||
- `DialogEventArgs`(`SepCore.Event`)
|
||||
- 当前 Dialog 只使用**一个事件类型**
|
||||
- 通过 `ButtonId` 区分按钮:
|
||||
- `1`:Confirm
|
||||
- `2`:Cancel
|
||||
- `3`:Other
|
||||
|
||||
### 6.2 当前数据流
|
||||
|
||||
1. 外部调用:
|
||||
|
||||
```csharp
|
||||
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.Base`:UI 专用事件
|
||||
- `SepCore.Runtime`:UI 基础抽象、`RawData`、`UseCase`、`UIRouterComponent`
|
||||
- `SepCore.Presentation`:Controller、Context、View
|
||||
|
||||
6. **Prefab 脚本一致性**
|
||||
Dialog prefab 上必须挂载插件内 `SepCore.UIModule.DialogForm`。
|
||||
|
||||
7. **Dialog 事件语义**
|
||||
当前 Dialog 使用单一 `DialogEventArgs` + `ButtonId` 区分按钮,不再使用多个按钮事件类型。
|
||||
|
||||
8. **Dialog 当前按单实例思路使用**
|
||||
`DialogController` 当前没有额外用 `sender` / `serialId` 做多实例过滤,默认按单实例 Dialog 使用。
|
||||
Reference in New Issue
Block a user