【整合】 框架使用规则

This commit is contained in:
Shiliang
2026-07-10 17:50:20 +08:00
parent db0143be83
commit 001d9e5ef5
45 changed files with 1716 additions and 4062 deletions

View File

@@ -1,129 +1,66 @@
# UI 框架
## 分类
- 项目框架
# UI 窗口系统
## 目的
记录 UIWindow 管理、窗口基类、UIMono 桥接、窗口资源加载和 UI 生命周期
记录 StrayFog 窗口管理、资源加载、生命周期、UIMono 绑定和虚拟列表的稳定使用规则
## 已知线索
## 主要路径
- 管理路径:`Assets/Game/GameHFScripte/CopyToHF/UIWindowMgr/`
- 窗口实现:`Assets/Game/GameHFScripte/GameFunction/UIWindows/`
- Mono 桥接:`Assets/Game/GameMono/Scripts/UIMono/`
- 资源路径:`Assets/Game/GameHFResource/CN/UIWindows/`
| 类型 | 路径 | 说明 |
|---|---|---|
| ESF 源码 | `Assets/StrayFog/Editor/CopyToX/CopyToHF/UIWindowMgr/` | 窗口框架真实源码 |
| HF 生成代码 | `Assets/Game/GameHFScripte/CopyToHF/UIWindowMgr/` | CopyToX 结果,禁止手改 |
| 业务窗口 | `Assets/Game/GameHFScripte/GameFunction/UIWindows/` | 窗口与子组件业务逻辑 |
| Mono 组件 | `Assets/Game/GameMono/Scripts/UIMono/` | SFUI/UIMono 和列表包装 |
| UI 资源 | `Assets/Game/GameHFResource/<语言>/UIWindows/` | Prefab 与窗口资源 |
## 已确认链路
UI manager 初始化:
```text
HFUIWindowMgr.OnAfterCreateInstance()
-> OnInitializeCanvasAndCamera()
-> 为 enHFUICanvasLayer 创建 Canvas
-> 为 ScreenSpaceCamera Canvas 创建 UI Camera
-> 创建 CacheSiblingIndex Canvas
-> 创建或接管 EventSystem / StandaloneInputModule
```
打开窗口:
## 打开流程
```text
HFFunCatalogue.UIManager.OpenWindow<T>()
-> HFUIWindowMgr.OnOpenWindow<HFUIWindowSetting>()
-> 创建 HFDefaultTask / HFUIWindow_RespondTask
-> 读取窗口类型上的 HFAssetBundlePathAttribute 和 HFUIWindowLayerAttribute
-> OnSettingWindowSiblingIndex()
-> 从缓存队列复用窗口实例,或 Activator.CreateInstance 创建窗口逻辑实例
-> OnPreloadWindowInMemory()
-> HFABMgr.Instance.LoadInMemory(windowPrefabPath)
-> win.BindGameObject(prefab)
-> OnAttachWindow(win)
-> win.SyncWindowState()
-> AbsHFUIWindow.OnOpen()
-> HFUIWindowMgr 创建窗口任务
-> 读取 HFAssetBundlePath / HFUIWindowLayer
-> 复用缓存或创建窗口逻辑
-> LoadInMemory 加载 Prefab
-> BindGameObject / OnAttachWindow
-> OnRunAwake实例首次绑定
-> OnOpen每次打开
```
关闭窗口
关闭窗口会执行 `OnClose`、隐藏实例并放入窗口缓存。需要释放窗口与资源时使用 UI 管理器公开卸载入口,不直接 Destroy 窗口 Prefab 或绕过管理器修改父节点和 siblingIndex。
```text
CloseWindow<T>() / AbsHFUIWindow.CloseWindow()
-> HFUIWindowMgr.OnCloseWindow()
-> siblingIndexLayer.ToggleWindowActive(false)
-> 放入 mCacheWindowInstanceMaping
-> 处理 backtrack 自动恢复窗口
-> win.SyncWindowState()
-> AbsHFUIWindow.OnClose()
```
## 生命周期
窗口资源绑定:
| 生命周期 | 用途 |
|---|---|
| `OnRunAwake` | 查找控件、绑定按钮、配置并初始化虚拟列表,只执行一次 |
| `OnOpen` | 注册本次打开所需事件、刷新数据与显示状态 |
| `OnClose` | 移除事件、停止计时器和清理本次打开状态 |
- 窗口类通过 `[HFAssetBundlePath(...)]` 标记 prefab 路径
- 窗口类通过 `[HFUIWindowLayer(...)]` 标记 Canvas/Layer 和是否允许多开。
- `AbsHFUIWindowSetting.SetWindowType` 会读取上述 attribute生成 `winId``winTypeId` 和资源路径。
异步加载、动画和下载的成功、失败、取消路径都必须结束窗口任务,不能让窗口永久停在加载中
## 使用规则
- 窗口必须继承 `AbsHFUIWindow`
- 新窗口必须配置 `HFAssetBundlePath``HFUIWindowLayer`
- 打开窗口统一走 `HFFunCatalogue.UIManager.OpenWindow<T>()`
- 关闭窗口统一走 `CloseWindow()``HFFunCatalogue.UIManager.CloseWindow<T>()`
- `OnOpen` 中注册的事件必须在 `OnClose` 中移除
- 不直接 `Instantiate` / `Destroy` UI 窗口 prefab
- 不绕过 `HFUIWindowMgr` 手动修改窗口 active、父节点或 siblingIndex
- 普通业务窗口默认使用 `Dynamic`,提示类优先使用 `Tooltip`,强交互弹窗优先使用 `Popup``Highest` 仅用于明确的顶层兜底。
- UI prefab 放在 `Assets/Game/GameHFResource/<语言>/UIWindows/<模块>/`,代码中的 `HFAssetBundlePath` 使用相对 `UIWindows/...` 的路径。
- 优先使用项目已有 UIMono / ZYUGUI 绑定流程,不手写大量 `transform.Find`
- 列表、循环列表、网格优先使用已有 ZYUGUI / SuperScrollView 组件和编辑器转换工具。
- 异步加载或动画结束后再调用完成回调时,必须保证失败/取消路径不会卡住窗口状态。
- 窗口继承 `AbsHFUIWindow`,配置 `HFAssetBundlePath``HFUIWindowLayer`
- 打开和关闭统一通过 `HFFunCatalogue.UIManager`
- `OnOpen``OnClose` 中的事件注册和释放保持对称
- 引用绑定优先使用项目 UIMono/SFUI 流程,不堆叠 `transform.Find`
- 单列虚拟列表使用 LoopListView2 包装接口,多列布局使用 LoopGridView 包装接口
- 模板节点在 Prefab 中保持禁用,由虚拟列表创建实例
- UI 修改必须验证脚本、Prefab 层级、RectTransform、组件状态和运行时效果
## 新增 UI 窗口清单
## 子文档
1. 确认窗口所属功能模块。
2. 创建窗口类,继承 `AbsHFUIWindow`
3. 添加 `HFAssetBundlePath``HFUIWindowLayer`
4. 创建或更新 prefab。
5. 使用 UIMono / ZYUGUI 生成或绑定 UI 引用。
6.`HFFunCatalogue.UIManager.OpenWindow<T>()` 接入入口。
7.`OnOpen` / `OnClose` 对称处理事件、计时器、临时数据。
8. 验证打开、关闭、重复打开、切场景或语言变化时的表现。
9. 如果新增模块或改变 UI 架构,更新相关模块文档。
## 梳理任务
- [x] 梳理窗口基类和打开/关闭 API。
- [x] 梳理窗口 prefab 路径绑定方式。
- [ ] 梳理 UIMono 自动绑定和 partial 生成关系。
- [x] 梳理窗口生命周期Open/Close/状态同步。
- [x] 梳理 UI 事件订阅和释放规范。
- [x] 梳理功能 UI 和框架 UI 的边界。
## 待确认
- 常规入口已确认是 `HFFunCatalogue.UIManager` / `HFUIWindowMgr`,是否存在绕过路径待继续扫描。
- UI 资源会经过 `HFAssetBundlePathAttribute` 和语言路径规则,哪些 UI 真正有 CN/EN 差异待确认。
- UIMono 是否由编辑器工具生成。
- `OnOpen`/`OnClose` 外是否还有业务窗口统一订阅/释放规范待确认。
## 相关模块
- [资源框架](../资源管理/README.md)
- [事件框架](../事件系统/README.md)
- [ZYUGUI Tools](../../模块索引.md#编辑器工具)
---
- [UI 开发指南](开发指南.md)
- [界面拼接](界面拼接.md)
- [UI 拼接工具](../编辑器工具/UI拼接工具.md)
## 文档关联
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
| 文档 | 关联原因 |
|---|---|
| [modules/模块索引.md](../../模块索引.md) | ZYUGUI Tools |
| [modules/frameworks/事件系统/README.md](../事件系统/README.md) | 事件框架 |
| [modules/frameworks/资源管理/README.md](../资源管理/README.md) | 资源框架 |
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 开发规则与框架/生成规则互相引用 |
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
| TODO补充依赖或使用本框架的模块文档 | 如依赖本框架的功能模块 |
| [模块索引](../../模块索引.md) | 模块入口和状态 |
| [资源管理](../资源管理/README.md) | UI Prefab 加载与卸载 |
| [事件系统](../事件系统/README.md) | 窗口事件生命周期 |
| [架构说明](../../../rules/10-架构说明.md) | ESF/HF 源码边界 |
| [开发规则](../../../rules/20-开发规则.md) | UI 与 Prefab 通用约束 |

View File

@@ -1,231 +1,135 @@
# StrayFog 框架 UI 开发指南
# UI 开发指南
## 一、窗口开发
### 1.1 创建模板
```csharp
[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE, "UIWindows/MyWindow/MyWindow.prefab")]
[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Dynamic)]
public class MyWindow : AbsHFUIWindow
{
private SFUI_Button BtnClose;
窗口类继承 `AbsHFUIWindow`,通过 attribute 声明资源路径和 Canvas 层级:
```csharp
[HFAssetBundlePath("UIWindows/ExampleWindow/ExampleWindow.prefab")]
[HFUIWindowLayer(enHFUICanvasLayer.Dynamic)]
public partial class ExampleWindow : AbsHFUIWindow
{
protected override void OnRunAwake()
{
base.OnRunAwake();
BtnClose = FindUICtrlByName<SFUI_Button>("BtnClose");
BtnClose.OnAddClickListener += OnBtnClose;
// 查找控件、绑定按钮、初始化列表。
}
protected override void OnOpen(Action _onOpenComplete)
protected override void OnOpen(object[] _args)
{
base.OnOpen(_onOpenComplete);
RefreshUI();
base.OnOpen(_args);
// 注册事件并刷新本次显示数据。
}
private void OnBtnClose(GameObject go) => CloseWindow();
private void RefreshUI() { }
}
```
### 1.2 生命周期
| 方法 | 调用时机 | 推荐操作 |
|-----|---------|---------|
| `OnRunAwake` | 创建时一次 | 绑定 UI 事件、初始化滚动列表 `BindEvent` |
| `OnOpen` | 每次打开 | 注册 TC 事件、调用 `ShowView()` 刷新数据 |
| `OnClose` | 每次关闭 | 取消 TC 事件 |
**关键原则**
- **绑定类操作**(如 `BindEvent`)放在 `OnRunAwake`,只需执行一次
- **刷新类操作**(如 `ShowView`)放在 `OnOpen`,每次打开都需执行
### 1.3 窗口操作
```csharp
// 打开
HFFunCatalogue.UIManager.OpenWindow<MyWindow>();
// 关闭
HFFunCatalogue.UIManager.CloseWindow<MyWindow>();
```
---
## 二、事件绑定
| 事件类型 | 绑定位置 | 是否需要 `-=` |
|---------|---------|--------------|
| UI 按钮/拖拽 | `OnRunAwake` | 不需要 |
| 全局 TC 事件 | `OnOpen`/`OnClose` | 需要 |
---
## 三、常用组件
### 3.1 按钮
```csharp
SFUI_Button btn = GetComponent<SFUI_Button>();
btn.OnAddClickListener += OnBtnClick;
btn.SetSelfGray(true);
```
### 3.2 文本
```csharp
SFUI_TextMeshProUGUI txt = GetComponent<SFUI_TextMeshProUGUI>();
txt.text = "Hello";
```
### 3.3 图片
```csharp
SFUI_Image img = GetComponent<SFUI_Image>();
img.HFSetSprite(/* atlasName */, "Icon_Player", /* languageRoot */);
```
### 3.4 滚动列表(核心)
**生命周期正确划分示例**
```csharp
private SFUI_ScrollRect m_ScrollList;
private List<ItemData> m_ItemList;
protected override void OnRunAwake()
{
base.OnRunAwake();
m_ScrollList = FindUICtrlByName<SFUI_ScrollRect>("ScrollList");
// ✅ 绑定事件放在 Awake一次绑定永久生效
m_ScrollList.BindEvent(
() => m_ItemList.Count, // 数据源闭包
(idx) => itemPrefab.gameObject, // 生成预制体
OnUpdateItem, // 更新回调
(item) => item.gameObject.HFGetOrAddSimulateBehaviour<ItemSlot>() // 获取组件
);
}
protected override void OnOpen(Action _onOpenComplete)
{
base.OnOpen(_onOpenComplete);
m_ItemList = GetDataFromServer(); // 获取最新数据
m_ScrollList.ShowView(); // ✅ 刷新显示放在 Open
}
private void OnUpdateItem(int index, AbsHFUIWindow window)
{
ItemSlot slot = window as ItemSlot;
slot.SetData(m_ItemList[index]);
}
```
**设计要点**
- `BindEvent``OnRunAwake` 中执行,建立绑定机制
- `ShowView``OnOpen` 中执行,触发数据刷新
- 数据源通过闭包引用,`ShowView` 时自动获取最新数据
---
## 四、对象池
| 场景 | 推荐方式 |
|-----|---------|
| 滚动列表 | `SFUI_ScrollRect.BindEvent`(内置池) |
| 固定栏(装备/技能) | `HFSimulateBehaviourPool<T>` |
---
## 五、子组件
### 5.1 多态基类
```csharp
public abstract class AbsListItem<T> : AbsHFUIWindow
{
protected SFUI_Button m_BtnItem;
protected Action<T> m_OnSelect;
protected override void OnRunAwake()
protected override void OnClose()
{
base.OnRunAwake();
m_BtnItem = gameObject.GetComponent<SFUI_Button>();
m_BtnItem.OnAddClickListener += OnBtnClick;
// 移除事件和计时器。
base.OnClose();
}
public virtual void SetData(T data, Action<T> onSelect = null)
{
m_OnSelect = onSelect;
OnDataChanged(data);
}
protected abstract void OnDataChanged(T data);
private void OnBtnClick(GameObject go) => m_OnSelect?.Invoke(m_CurrentData);
}
// 子类
public class CardItem : AbsListItem<CardData>
{
protected override void OnDataChanged(CardData data) { }
}
```
---
业务打开窗口使用:
## 六、开发规范
```csharp
HFFunCatalogue.UIManager.OpenWindow<ExampleWindow>();
```
### 6.1 文件路径
不要直接实例化窗口 Prefab也不要直接调用内部 `HFUIWindowMgr` 单例。
#### 6.1.1 核心资源路径
## 二、生命周期规则
| 类型 | 路径 | 说明 |
|-----|------|------|
| **窗口脚本** | `Assets/Game/GameHFScripte/GameFunction/UIWindows/{WindowName}/` | 窗口逻辑脚本存放目录 |
| **窗口预制体** | `Assets/Game/GameHFResource/CN/UIWindows/{WindowName}/` | UI 窗口预制体存放目录 |
| **图标图集** | `Assets/Game/GameHFResource/CN/SpriteAtlas/{AtlasName}/` | SpriteAtlas 图集资源 |
| **字体资源** | `Assets/Game/GameHFResource/CN/Fonts/` | 字体文件存放目录 |
- `OnRunAwake`:实例首次绑定时执行一次,适合控件查找、按钮绑定和虚拟列表初始化。
- `OnOpen`:每次打开执行,适合注册事件、应用参数和刷新数据数量。
- `OnClose`:每次关闭执行,与 `OnOpen` 中的监听、计时器和临时状态对称清理。
- 列表初始化不能依赖数据已经到达;先以数量 `0` 初始化,数据准备后再刷新数量。
#### 6.1.2 工具脚本路径
## 三、单列虚拟列表
| 工具名称 | 路径 | 职责 |
|---------|------|------|
| **UIPrefabGenerator** | `Assets/Editor/Command/UIPrefabGenerator.cs` | JSON 转 UI 预制体 |
| **UIPrefabToJson** | `Assets/Editor/Command/UIPrefabToJson.cs` | 预制体转 JSON |
| **RemoteCommand** | `Assets/Editor/Command/RemoteCommand.cs` | 外部命令接口 |
| **ExternalCommandListener** | `Assets/Editor/Command/ExternalCommandListener.cs` | 外部命令监听器 |
单列或单行滚动使用 LoopListView2 包装接口:
#### 6.1.3 文档路径
```csharp
m_List.SetLoopListViewItemPrefabs(
new GameObject[] { m_ItemTemplate.gameObject }, 0, 1);
| 文档名称 | 路径 |
|---------|------|
| UI 开发指南 | [UI窗口系统/开发指南.md](开发指南.md) |
| UI 界面拼接设计指南 | [UI窗口系统/界面拼接.md](界面拼接.md) |
m_List.InitListView(
0,
OnGetItem,
OnCreateItem,
OnUpdateItem,
OnGetPrefabIndex,
false);
```
#### 6.1.4 命令触发路径
数据准备或变化后:
- **触发文件**: `{ProjectRoot}/Trigger_Command.txt`
- **JSON 配置**: `Assets/Editor/UI/{WindowName}.json`
```csharp
m_List.RefreshDataTotalCount(m_DataList == null ? 0 : m_DataList.Count);
```
### 6.2 性能优化
- 使用对象池复用 UI 元素
- 将图标打包到 SpriteAtlas
- 滚动列表用 `SFUI_ScrollRect.BindEvent`
回调职责:
---
| 回调 | 职责 |
|---|---|
| `OnGetItem` | 从已创建节点取得 `SFUI_ScrollRectItem` |
| `OnCreateItem` | 首次创建绑定类和 SimulateBehaviour |
| `OnUpdateItem` | 根据 dataIndex 更新显示,不能假设旧数据仍有效 |
| `OnGetPrefabIndex` | 多模板时返回模板索引,单模板返回 `0` |
**版本**: v1.1
**适用**: StrayFog 框架 UI 开发人员
垂直列表模板通常使用左上锚点和 pivot `(0, 1)`,宽度与 viewport 匹配;水平列表按滚动方向设置。模板尺寸或 pivot 错误会造成重叠、偏移或不可见。
---
## 四、多列虚拟列表
存在固定列数、`GridLayoutGroup` 语义或二维排布时使用 LoopGridView
```csharp
m_Grid.SetLoopGridViewItemPrefabs(
new GameObject[] { m_ItemTemplate.gameObject },
new Vector2(216, 293),
new Vector2(50, 30),
new RectOffset(55, 55, 0, 0),
4,
1);
m_Grid.InitGridView(
0,
OnGetGridItem,
OnCreateGridItem,
OnUpdateGridItem,
OnGetGridPrefabIndex);
```
刷新数量:
```csharp
m_Grid.RefreshGridDataTotalCount(m_DataList == null ? 0 : m_DataList.Count);
```
不要通过单列 ListView 模拟多列布局。Grid 的 itemSize、spacing、padding 和列数必须与 Prefab 视觉布局一致。
## 五、Prefab 设置
- 模板节点保持 `SetActive(false)`,但必须位于窗口层级中以便引用收集。
- `SFUI_ScrollRect`、viewport、content 和模板引用必须完整。
- 虚拟列表初始化会关闭旧 `LayoutGroup``ContentSizeFitter`,运行时布局由 SuperScrollView 管理。
- 不要因为这些旧组件在运行时被关闭就重新勾选或依赖它们计算虚拟列表尺寸。
- 检查模板上的 `LoopListViewItem2``LoopGridViewItem`;包装层会补组件,但 Prefab 结构仍需正确。
- UIMono 收集需要包含 inactive 子节点,不能只扫描激活节点。
## 六、验证清单
1. 构建 `SFMono.csproj` 和涉及的 `GameHF.csproj`
2. 刷新 Unity Assets 并读取 Console。
3. 检查 Prefab 的 viewport、content、模板、RectTransform 和组件启用状态。
4. 验证空数据、1 个元素、超出视口、多次刷新和重复打开。
5. 滚动后验证元素回收与复用,没有 `KeyNotFoundException`、旧数据显示或重复创建。
6. 多列列表验证最后一行不足列数时的尺寸和定位。
## 文档关联
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
| 文档 | 关联原因 |
|---|---|
| [UI 窗口系统入口](README.md) | 本文件是 UI 窗口系统模块的子文档 |
| [UI 界面拼接设计指南](界面拼接.md) | 界面拼接是 UI 开发的补充设计指南 |
| [UI 拼接工具](../编辑器工具/UI拼接工具.md) | JSON 驱动 UI 生成工具说明 |
| [事件系统](../事件系统/README.md) | UI 事件订阅和释放规范 |
| [对象池与运行时对象管理](../内存对象管理/README.md) | UI 元素复用相关 |
| [资源管理](../资源管理/README.md) | UI 资源加载依赖资源管理 |
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | UI 命名与编码规范互相引用 |
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
| [UI 窗口系统](README.md) | 窗口框架入口与生命周期 |
| [界面拼接](界面拼接.md) | Prefab 结构和视觉实现 |
| [UI 拼接工具](../编辑器工具/UI拼接工具.md) | JSON/Prefab 双向转换 |
| [开发规则](../../../rules/20-开发规则.md) | 通用 UI 和 C# 规则 |

View File

@@ -1,192 +1,80 @@
# UI 界面拼接设计指南
# UI 界面拼接
## 一、界面结构设计原则
## 一、输入与目标
### 1.1 模块化拆分
开始拼接前同时确认:
将界面按功能划分为独立模块:
- 设计原图、标注图或 PSD 中的尺寸、间距和层级。
- 项目现有 Sprite、SpriteAtlas、字体、材质和可复用 Prefab。
- 目标窗口名、Prefab 路径、Canvas 层级和脚本绑定方式。
- 目标分辨率、SafeArea、横竖屏和滚动区域。
| 模块类型 | 功能定位 | 示例 |
|---------|---------|------|
| **信息展示区** | 显示玩家状态、资源等静态信息 | 顶部信息栏、左侧状态栏 |
| **功能入口区** | 提供功能按钮入口 | 中央功能区、底部快捷栏 |
| **交互面板区** | 可展开/收起的功能面板 | 右侧功能面板、顶部按钮区 |
| **输入输出区** | 用户输入和系统反馈 | 聊天输入框、提示信息 |
不能只凭截图选择近似资源。设计图中的可检查对象必须使用语义正确的现有资源;缺失资源应明确记录,不能用无关图片顶替。
### 1.2 经典布局模式
## 二、推荐层级
```
┌─────────────────────────────────────────────────────┐
│ TopArea (信息展示 + 可收起功能按钮) │
├──────────┬───────────────────────┬──────────────────┤
│ Left │ │ Right │
Area │ CenterArea │ Area │
(状态) │ (核心功能入口) │ (功能面板) │
│ │ │
├──────────┴───────────────────────┴──────────────────┤
BottomArea (快捷功能 + 状态信息 + 输入) │
─────────────────────────────────────────────────────┘
```text
WindowName
├── ImgBg
├── HeaderArea
├── ContentArea
├── StaticContent
└── ListView
└── viewport
│ └── content
└── ItemTemplate (inactive)
── FooterArea
└── PopupOrEffectArea
```
---
- 按视觉和功能边界组织节点,不为每个图片增加无意义容器。
- 节点命名应能对应脚本字段和功能,例如 `Btn_Close``Txt_Title``Img_Icon`
- 复用 Item 或子面板时保持独立根节点,便于 UIMono 绑定和对象复用。
- 全屏背景使用稳定锚点;局部模块使用明确尺寸,避免依赖编辑器当前分辨率。
## 二、组件拆分方法
## 三、列表选择
### 2.1 基础组件识别
- 单列或单行滚动LoopListView2。
- 固定多列或网格LoopGridView。
- 模板节点保持禁用尺寸、pivot 和滚动方向必须匹配。
- 虚拟列表不依赖旧 LayoutGroup/ContentSizeFitter 运行时排版。
- 具体初始化接口见 [UI 开发指南](开发指南.md)。
| 组件类型 | 识别特征 | 处理方式 |
|---------|---------|---------|
| **按钮** | 可点击、有交互反馈 | 使用 `SFUI_Button` |
| **文本** | 静态/动态文字显示 | 使用 `SFUI_TextMeshProUGUI` |
| **图片** | 图标、背景图 | 使用 `SFUI_Image` |
| **进度条** | HP、MP、经验等 | 使用 `SFUI_Slider` |
| **列表** | 可滚动的项目列表 | 使用 `SFUI_ScrollRect` |
## 四、拼接流程
### 2.2 复合组件封装
1. 读取 [UnityAI README](../../../README.md) 中的 UI 入口和相关指南。
2. 检查目标 Prefab 是否已存在,导出或读取当前层级,避免覆盖有效结构。
3. 枚举目标资源目录和图集,建立设计元素到实际资源的对应关系。
4. 确定窗口根尺寸、锚点、主要功能区和列表类型。
5. 使用 Unity Editor 手工拼接,或通过 MCP 的 JSON 生成工具创建基础结构。
6. 绑定 SFUI/UIMono 组件与业务脚本,补齐交互、列表和动态数据。
7. 刷新资源并检查 Console、Prefab 序列化结构和运行时显示。
8. 对照原图检查尺寸、层级、颜色、字体、九宫格和点击区域。
将功能相关的基础组件组合为复合组件,便于复用和维护:
JSON 生成适合批量创建稳定层级和组件,不代替视觉校对、业务绑定和运行时验证。
**设计原则**
- 将同一功能模块的组件封装在一起(如玩家头像+血条+蓝条)
- 提供统一的数据设置接口
- 保持组件之间的逻辑独立性
## 五、修改已有 Prefab
---
- 先读取现有 Prefab 和脚本引用,再调整层级或组件。
- 移动、重命名节点前搜索 `FindUICtrlByName`、序列化字段和生成绑定代码。
- 只修改目标节点,避免让 Unity 重序列化无关 Prefab。
- 组件勾选状态属于 Prefab 行为的一部分;修复显示问题时同时检查 `enabled` 与 GameObject active。
- 列表迁移后,旧布局组件可保留序列化参数作为参考,但运行时由新列表包装关闭。
## 三、预制体层级规范
## 六、验收
### 3.1 标准层级结构
```
WindowName (RectTransform)
├── ModuleName (RectTransform) # 模块容器
│ ├── ComponentName (SFUI_*) # 功能组件
│ ├── SubModule (RectTransform) # 子模块
│ └── CollapsibleArea (RectTransform) # 可收起区域
└── BtnCollapse (SFUI_Button) # 收起控制按钮
```
### 3.2 命名规范
| 类型 | 命名规则 | 示例 |
|-----|---------|------|
| 模块容器 | PascalCase + Area | `TopArea` |
| 功能组件 | PascalCase | `BtnCollapseTop` |
| 可收起区域 | PascalCase + FuncArea | `TopFuncArea` |
| 状态变量 | m_ + PascalCase | `m_IsTopCollapsed` |
---
## 四、收起/展开功能设计
### 4.1 功能定义
| 按钮 | 控制区域 | 用途 |
|-----|---------|------|
| `BtnCollapseTop` | 顶部功能按钮区 | 收起/展开顶部按钮群 |
| `BtnCollapseRight` | 右侧功能面板区 | 收起/展开右侧功能面板 |
### 4.2 设计要点
**状态管理**:每个可收起区域需要一个布尔状态变量来记录当前收起/展开状态。
**交互逻辑**
1. 点击收起按钮时,切换对应区域的显示/隐藏状态
2. 同时切换按钮图标(收起时显示展开图标,展开时显示收起图标)
**布局要求**
- 收起按钮应放置在对应区域的边缘位置,便于用户找到
- 收起区域内的所有元素应作为一个整体进行显示/隐藏控制
- 收起状态应在窗口生命周期内保持记忆
---
## 五、实际案例:主界面拼接
### 5.1 模块化设计思想
**核心原则**:将界面按功能边界划分为独立模块,模块内可嵌套子模块。
**模块归类方法**
1. **一级模块**:按空间位置划分(顶部、左侧、中央、右侧、底部)
2. **二级模块**:按功能类型划分(信息展示区、功能按钮区、输入输出区)
3. **三级模块**:按具体功能划分(玩家头像、资源显示、功能按钮群)
**层级关系示例**
```
Window
├── TopArea (一级:位置)
│ ├── InfoPanel (二级:信息展示)
│ │ └── TxtWelcome (三级:具体组件)
│ └── FuncButtons (二级:功能按钮)
│ └── BtnUpload (三级:具体按钮)
└── LeftArea (一级:位置)
└── PlayerPortrait (二级:复合组件)
├── ImgAvatar (三级:头像)
└── BarHP (三级:血条)
```
**设计要点**
- 同一功能域内的元素归为同一模块(如头像、血条、蓝条归为玩家状态模块)
- 独立交互功能作为独立模块(如收起按钮单独作为控制模块)
- 可收起区域作为独立子模块,便于统一控制显示/隐藏
---
## 六、界面拼接流程
### 6.1 设计阶段
1. **需求分析**:明确界面功能和交互需求
2. **模块划分**:将界面拆分为独立功能模块
3. **层级设计**:设计预制体层级结构
### 6.2 开发阶段
1. **组件创建**:创建基础组件和复合组件
2. **预制体搭建**:按层级结构搭建预制体
3. **脚本编写**:实现交互逻辑和收起/展开功能
### 6.3 测试阶段
1. **功能测试**:验证所有按钮和交互功能
2. **收起测试**:测试收起/展开功能正常工作
3. **适配测试**:验证不同分辨率下的显示效果
---
## 七、注意事项
1. **组件复用**:相同功能的组件尽量复用,减少冗余
2. **状态管理**:收起状态需要正确保存和恢复
3. **图标反馈**:收起按钮图标应随状态变化
4. **性能优化**:可收起区域使用 `SetActive` 而非销毁重建
5. **命名规范**:统一的命名规则便于维护和查找
---
## 八、JSON 配置驱动的 UI 生成方式
本项目支持通过 JSON 配置快速生成 UI 预制体,详细工具说明、命令格式和部署步骤见 [UI 拼接工具](../编辑器工具/UI拼接工具.md)。
UI 相关人员核心工作流程:读取设计效果图和碎片图片,生成 JSON 配置文件,再通过工具自动生成 Unity 预制体。
---
**版本**: v1.2
**适用**: StrayFog 框架 UI 拼接开发人员
---
- Prefab 路径和窗口 attribute 一致。
- Sprite、字体、材质和图集引用有效。
- 锚点、pivot、sizeDelta 和层级与设计目标一致。
- 按钮、文本、列表、进度和弹窗交互正确。
- 空数据、极长文本、不同分辨率和重复打开不破版。
- Unity Console 无本次新增 Error运行时实际显示通过确认。
## 文档关联
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
| 文档 | 关联原因 |
|---|---|
| [UI 窗口系统入口](README.md) | 本文件是 UI 窗口系统模块的子文档 |
| [UI 开发指南](开发指南.md) | 窗口开发与界面拼接互相补充 |
| [UI 拼接工具](../编辑器工具/UI拼接工具.md) | JSON 驱动 UI 生成工具说明 |
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | UI 命名与资源规则互相引用 |
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
| [UI 窗口系统](README.md) | 窗口框架和资源路径 |
| [UI 开发指南](开发指南.md) | 生命周期和虚拟列表接口 |
| [UI 拼接工具](../编辑器工具/UI拼接工具.md) | MCP JSON/Prefab 工具 |
| [MCP 使用规则](../编辑器工具/MCP工具/CrystalBattle_Codex_Unity_MCP使用规则.md) | Codex 与 Unity 的调用规则 |