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

10 KiB
Raw Blame History

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);
}

使用流程

  1. OnRunAwake 中调用 BindEvent 绑定数据
  2. OnOpen 或数据更新时调用 ShowView 刷新显示
  3. 通过 ScrollToIndexverticalNormalizedPosition 控制滚动位置

注意SFUI_ScrollRect 已内置对象池机制,无需额外使用 HFSimulateBehaviourPool 实现循环列表。


四、对象池模式

注意循环滚动列表请使用 SFUI_ScrollRect.BindEventHFSimulateBehaviourPool 适用于非滚动场景的对象复用。

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框架项目业务开发人员