Files
UnityAI/03_核心功能/UI开发指南.md

352 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# StrayFog 框架 UI开发指南
## 一、UI窗口开发
### 1.1 窗口创建模板
```csharp
[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE, "UIWindows/MyWindow/MyWindow.prefab")]
[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Dynamic, isIgnoreCloseByESC = false)]
public class MyWindow : AbsHFUIWindow
{
// UI组件
private SFUI_Button BtnClose;
private SFUI_TextMeshProUGUI TxtTitle;
protected override void OnRunAwake()
{
base.OnRunAwake();
// 初始化UI组件引用
BtnClose = FindUICtrlByName<SFUI_Button>("BtnClose");
TxtTitle = FindUICtrlByName<SFUI_TextMeshProUGUI>("TxtTitle");
// 绑定按钮事件OnRunAwake只执行一次
BtnClose.OnAddClickListener += OnBtnClose;
}
protected override void OnOpen(Action _onOpenComplete)
{
base.OnOpen(_onOpenComplete);
// 注册全局事件监听(每次打开都要注册)
HFFunCatalogue.Event.TCEvent.AddListener<HFEvent_DataUpdate>(
enHFCTCEvent.DataUpdate, OnDataUpdate);
// 初始化数据显示
RefreshUI();
}
protected override void OnClose(Action _onCloseComplete)
{
// 取消全局事件监听(每次关闭都要取消)
HFFunCatalogue.Event.TCEvent.RemoveListener<HFEvent_DataUpdate>(
enHFCTCEvent.DataUpdate, OnDataUpdate);
base.OnClose(_onCloseComplete);
}
private void OnBtnClose(GameObject go) => CloseWindow();
private void OnDataUpdate(IHFEventArg<enHFCTCEvent> arg) => RefreshUI();
private void RefreshUI() { /* 更新UI显示 */ }
}
```
### 1.2 窗口生命周期
| 方法 | 调用时机 | 说明 | 推荐操作 |
|-----|---------|------|---------|
| `OnRunAwake` | 窗口创建时调用一次 | 初始化组件引用 | **绑定按钮事件、初始化对象池** |
| `OnRunStart` | OnRunAwake之后调用一次 | 初始化数据 | **配置数据加载** |
| `OnOpen` | 每次打开窗口时调用 | 注册事件、刷新数据 | **注册TC事件、初始化数据显示** |
| `OnClose` | 每次关闭窗口时调用 | 取消事件、清理资源 | **取消TC事件、回收对象池** |
| `OnDestroy` | 窗口销毁时调用 | 释放资源 | **释放非托管资源** |
### 1.3 窗口操作
```csharp
// 打开窗口
HFFunCatalogue.UIManager.OpenWindow<MyWindow>();
// 打开窗口并获取实例
HFFunCatalogue.UIManager.OpenWindow<MyWindow>((win, args) => {
// win: 窗口实例
});
// 关闭窗口
HFFunCatalogue.UIManager.CloseWindow<MyWindow>();
```
---
## 二、事件绑定规范
### 2.1 事件绑定位置
| 事件类型 | 推荐位置 | 原因 | 是否需要 `-=` 防重复 |
|---------|---------|------|-------------------|
| **UI按钮事件** | `OnRunAwake` | 静态稳定,只需注册一次 | ❌ 不需要 |
| **列表拖拽事件** | `OnRunAwake` | 窗口生命周期内持续响应 | ❌ 不需要 |
| **全局TC事件** | `OnOpen`/`OnClose` | 窗口关闭后不应响应 | ✅ 需要 |
| **数据刷新逻辑** | `OnOpen` | 每次打开窗口需刷新 | - |
---
## 三、常用UI组件
### 3.1 SFUI_Button组件
```csharp
SFUI_Button btn = GetComponent<SFUI_Button>();
// 添加事件
btn.OnAddClickListener += OnBtnClick;
btn.OnAddEnterListener += OnBtnEnter;
btn.OnAddExitListener += OnBtnExit;
// 设置状态
btn.SetSelfGray(true); // 灰色不可用
btn.interactable = false; // 不可交互
btn.SetSelected(true); // 选中状态
```
### 3.2 SFUI_TextMeshProUGUI组件
```csharp
SFUI_TextMeshProUGUI txt = GetComponent<SFUI_TextMeshProUGUI>();
txt.text = "Hello World";
txt.color = Color.red;
txt.fontSize = 24;
txt.outlineColor = Color.black;
txt.outlineWidth = 2;
// 动态调整高度
txt.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, txt.preferredHeight);
```
### 3.3 SFUI_Image组件
```csharp
SFUI_Image img = GetComponent<SFUI_Image>();
// 加载Sprite
img.HFSetSprite(enHFSpriteAtlas.ASSETS_GAME_GAMEHFRESOURCE_SPRITEATLAS_ITEM,
"Icon_Player",
enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE);
// 设置透明度
img.SetAlpha(0.5f);
// 设置灰度
img.SetSelfGray(true);
// 清空Sprite
img.HFClearSprite();
```
### 3.4 SFUI_ScrollRect组件列表滚动
SFUI_ScrollRect 专门用于处理多物品的上下滑动列表,**内置对象池机制实现循环列表**,提供高效的列表渲染和滚动管理。
```csharp
SFUI_ScrollRect scroll = GetComponent<SFUI_ScrollRect>();
// 绑定列表数据(核心方法)
scroll.BindEvent(
() => itemList.Count, // 数据数量
(idx) => itemPrefab.gameObject, // 获取模板对象
OnUpdateItem, // 更新回调
(item) => item.gameObject.HFGetOrAddSimulateBehaviour<ItemSlot>() // 获取组件
);
// 刷新列表显示
scroll.ShowView();
// 滚动到指定位置
scroll.ScrollToIndex(0); // 滚动到第N项
scroll.verticalNormalizedPosition = 0; // 滚动到底部1=顶部0=底部)
```
**BindEvent 参数说明**
| 参数 | 类型 | 说明 |
|-----|------|------|
| 1 | `Func<int>` | 返回数据总数 |
| 2 | `Func<int, GameObject>` | 根据索引返回模板对象 |
| 3 | `Action<SFUI_ScrollRectItem>` | 列表项更新回调 |
| 4 | `Func<SFUI_ScrollRectItem, AbsHFUIWindow>` | 获取列表项组件 |
**更新回调示例**
```csharp
private void OnUpdateItem(SFUI_ScrollRectItem _item)
{
ItemSlot slot = _item.simulateBehaviour as ItemSlot;
ItemData data = itemList[_item.dataIdx];
slot.SetData(data, OnItemClick);
}
```
**使用流程**
1.`OnRunAwake` 中调用 `BindEvent` 绑定数据
2.`OnOpen` 或数据更新时调用 `ShowView` 刷新显示
3. 通过 `ScrollToIndex``verticalNormalizedPosition` 控制滚动位置
> **注意**SFUI_ScrollRect 已内置对象池机制,**无需额外使用 HFSimulateBehaviourPool** 实现循环列表。
---
## 四、对象池模式
> **注意****循环滚动列表请使用 `SFUI_ScrollRect.BindEvent`**HFSimulateBehaviourPool 适用于非滚动场景的对象复用。
### 4.1 对象池初始化
```csharp
HFSimulateBehaviourPool<ItemSlot> m_ItemPool = null;
protected override void OnRunAwake()
{
base.OnRunAwake();
SFUI_Button itemPrefab = FindUICtrlByName<SFUI_Button>("ItemPrefab");
SFUI_Behaviour parentContainer = FindUICtrlByName<SFUI_Behaviour>("ParentContainer");
// 创建对象池
m_ItemPool = new HFSimulateBehaviourPool<ItemSlot>(
itemPrefab.gameObject, // 模板对象
parentContainer.gameObject // 父容器
);
}
```
### 4.2 对象池使用(适用于固定数量场景)
```csharp
private void RefreshItems()
{
// 回收所有已使用对象
m_ItemPool.RecycUsingObject();
// 遍历数据创建新对象
foreach (var data in itemDataList)
{
ItemSlot slot = m_ItemPool.GetItem(out bool isNew);
slot.SetData(data, OnItemClick);
}
}
// 适用于固定数量的装备栏、技能栏等场景
private void ShowEquipedCards()
{
m_CardItems.RecycUsingObject();
for (int i = 0; i < 5; i++)
{
var item = m_CardItems.GetItem(out bool isNew);
// 设置数据...
}
}
```
### 4.3 适用场景对比
| 场景 | 推荐方式 | 说明 |
|-----|---------|------|
| **滚动列表** | `SFUI_ScrollRect.BindEvent` | 自动实现循环复用 |
| **固定数量栏** | `HFSimulateBehaviourPool` | 如装备栏、技能栏5个固定位置 |
| **动态弹窗** | `HFSimulateBehaviourPool` | 需要频繁创建销毁的弹窗 |
---
## 五、子组件开发
子组件作为列表项使用,继承 `AbsHFUIWindow`,核心特点:
| 特点 | 说明 |
|-----|------|
| **数据传递** | 通过 `SetData` 方法接收数据 |
| **回调机制** | 通过 `Action<T>` 与父组件通信 |
| **对象池** | 配合 `HFSimulateBehaviourPool<T>` 实现复用 |
---
## 六、UI开发规范
### 6.1 文件放置结构
UI相关文件应按照以下规范组织
**脚本文件**.cs
```
Assets/Game/GameHFScripte/GameFunction/UIWindows/
├── BagWindow/
│ └── BagWindow.cs # 窗口脚本
├── MainWindow/
│ └── MainWindow.cs
└── Common/
└── ItemSlot.cs # 公共子组件脚本
```
**预制体文件**.prefab
```
Assets/Game/GameHFResource/CN/UIWindows/
├── BagWindow/
│ └── BagWindow.prefab # 窗口预制体
├── MainWindow/
│ └── MainWindow.prefab
└── Common/
└── ItemSlot.prefab # 公共子组件预制体
```
**图片资源**按UI名称分类
```
Assets/Game/GameHFResource/CN/SpriteAtlas/
├── BagWindow/ # 背包窗口相关图片
│ ├── Icon_Bag.png
│ └── Btn_Close.png
├── MainWindow/ # 主窗口相关图片
│ └── Btn_Menu.png
└── Common/ # 公共图片
└── Btn_Confirm.png
```
### 6.2 窗口属性配置
每个窗口必须配置以下属性:
```csharp
[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE,
"UIWindows/BagWindow/BagWindow.prefab")]
[HFUIWindowLayer(enHFUICanvasLayer.UIWindow,
enHFUIWindowLayer.Dynamic,
isIgnoreCloseByESC = false)]
public class BagWindow : AbsHFUIWindow
{
// ...
}
```
**路径对应关系**
- 属性路径:`UIWindows/BagWindow/BagWindow.prefab`
- 实际路径:`Assets/Game/GameHFResource/CN/UIWindows/BagWindow/BagWindow.prefab`
**属性说明**
| 属性 | 作用 | 必填 |
|-----|------|------|
| `HFAssetBundlePath` | 指定预制体路径,用于资源加载 | ✅ |
| `HFUIWindowLayer` | 指定窗口层级和行为 | ✅ |
| `isIgnoreCloseByESC` | 是否忽略ESC关闭 | ❌ |
### 6.3 性能优化建议
| 优化项 | 说明 | 实现方式 |
|-------|------|---------|
| **对象池复用** | 频繁创建的UI元素使用对象池 | 使用 `HFSimulateBehaviourPool<T>` |
| **图集打包** | 将小图标打包到SpriteAtlas | 在Unity中配置SpriteAtlas |
| **层级优化** | 合理设置Canvas层级 | 使用 `HFUIWindowLayer` 属性 |
| **避免过度绘制** | 减少重叠UI元素 | 合理设计UI布局 |
| **批量操作** | 使用RectTransform批量修改 | 禁用后批量修改再启用 |
| **列表优化** | 使用SFUI_ScrollRect绑定模式 | 自动复用列表项 |
---
**版本**: v1.0
**适用范围**: StrayFog框架项目业务开发人员