10 KiB
10 KiB
StrayFog 框架 UI开发指南
一、UI窗口开发
1.1 窗口创建模板
[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 窗口操作
// 打开窗口
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组件
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组件
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组件
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 专门用于处理多物品的上下滑动列表,内置对象池机制实现循环列表,提供高效的列表渲染和滚动管理。
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> |
获取列表项组件 |
更新回调示例:
private void OnUpdateItem(SFUI_ScrollRectItem _item)
{
ItemSlot slot = _item.simulateBehaviour as ItemSlot;
ItemData data = itemList[_item.dataIdx];
slot.SetData(data, OnItemClick);
}
使用流程:
- 在
OnRunAwake中调用BindEvent绑定数据 - 在
OnOpen或数据更新时调用ShowView刷新显示 - 通过
ScrollToIndex或verticalNormalizedPosition控制滚动位置
注意:SFUI_ScrollRect 已内置对象池机制,无需额外使用 HFSimulateBehaviourPool 实现循环列表。
四、对象池模式
注意:循环滚动列表请使用
SFUI_ScrollRect.BindEvent,HFSimulateBehaviourPool 适用于非滚动场景的对象复用。
4.1 对象池初始化
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 对象池使用(适用于固定数量场景)
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 窗口属性配置
每个窗口必须配置以下属性:
[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框架项目业务开发人员