项目开发日记

框架整理学习

UIMgr:

一、数据结构与算法

1.1 关键数据结构

成员变量 类型 说明
m_Ctrls List<PageInfo> 当前正在显示的所有 UI 页面
m_Caches List<PageInfo> 已打开过、但现在不显示的页面(缓存池)

1.2 算法逻辑

  • 查找缓存页面 :从 m_Caches 中倒序查找是否已有对应 ePageType 页面,找到则重用。

  • 页面加载 :从资源管理器 ResMgr 加载 prefab 并绑定控制器/视图组件。

  • 页面关闭 :从 m_Ctrls 移除,添加到 m_Caches,供后续复用。

  • 刷新页面:关闭当前页面再重新打开。

这些逻辑大多采用线性搜索(O(n)),足够应对中小型游戏中的 UI 数量。

二、基本语法与结构说明

2.1 类继承

cs 复制代码
public class UIMgr : Singleton<UIMgr>

继承自泛型单例 Singleton<T>,保证 UIMgr 全局唯一实例,可通过 UIMgr.Inst 获取。

2.2 组件挂载与初始化

cs 复制代码
ResMgr.Inst.Get(name, null, Constant.m_UiPath).AddComponent<PageInfo>();

加载 UI prefab 后添加 PageInfo 组件,并手动获取并设置 BaseCtrlBaseView

2.3 控制器与视图绑定

cs 复制代码
ctrl.m_PageInfo = showPage;
view.m_PageInfo = showPage;

通过双向引用让页面、控制器和视图互相关联。

✅ 三、实现思路详解

3.1 UI 生命周期管理

cs 复制代码
view.Init();
ctrl.Init();
ctrl.Show();
view.Show();
  • 初始化(Init):绑定数据、设置 UI 元素初始状态

  • 显示(Show):打开 UI 页面

  • 隐藏(UnShow):关闭 UI 页面,但不销毁对象

3.2 缓存复用机制

UI 关闭时并不直接销毁,而是放入 m_Caches 中,节省加载资源开销,提高性能。

3.3 事件广播机制

cs 复制代码
EventMgr.Inst.Broadcast(IntEvt.Fetch(EventId.OpenUI, ...));

3.4 灵活的关闭方式(函数重载)

支持通过:

  • 页面类型(EPageType

  • 控制器(BaseCtrl

  • 视图(BaseView

  • 页面信息(PageInfo

等不同方式关闭页面。

✅ 四、补充建议与未来优化方向

数据结构优化

问题 建议
线性查找效率较低 m_Ctrlsm_Caches 改为 Dictionary<EPageType, PageInfo>,可快速查找
页面缓存永不销毁,可能内存溢出 定期检查 m_Caches 长度,进行清理(LRU 算法或设置上限)

解耦结构设计

当前逻辑是"管理器 + 控制器 + 视图"三层结构,推荐引入更标准的 UI 框架设计模式,例如:

  • MVC(Model-View-Controller)

  • MVVM(Model-View-ViewModel)

  • ECS(Entity Component System) ------ 用于大型项目

异步加载支持
如果资源加载较慢,建议将:

cs 复制代码
ResMgr.Inst.Get(...)

替换为异步方法,支持 loading 动画和取消操作。

动态层级系统

目前层级(sortingOrder)是注释掉的,建议恢复并基于m_Crrls.Count自动设置:

cs 复制代码
showPage.GetComponent<UIPanel>().sortingOrder = m_Ctrls.Count;

确保 UI 正确叠加,避免层级错乱。

✅ 五、总结:

优点 💡 不足点 ⚠️
简洁明了、结构清晰 查找效率低、资源管理偏静态
支持多种关闭方式 缓存机制缺乏清理策略
模块化控制器与视图 控制器与视图绑定写死在框架中
✅ 一、什么是 Dictionary<EPageType, PageInfo>

Dictionary<K, V> 是 C# 中的一种泛型集合类 ,也叫 字典哈希表 ,它存储的是 "键-值对(Key-Value Pair)"

cs 复制代码
Dictionary<K, V>
  • K 是键(Key)类型,必须是唯一的。

  • V 是值(Value)类型,可以重复。

    在你的场景中:

    cs 复制代码
    Dictionary<EPageType, PageInfo>
  • EPageType 是一个枚举类型(表示页面类型,例如:主页、背包、商店)

  • PageInfo 是页面的信息类,包含了 ControllerView 以及一些元数据。

    ✅ 举个例子:

    cs 复制代码
    var dic = new Dictionary<EPageType,PageInfo>();
    
    dic[EPageType.Home] = homePageInfo;
    dic[EPageType.Shop] = shopPageInfo;
    
    //获取
    var page = dic[EPageType.Home];

    相比于 List<PageInfo>,使用 Dictionary 的最大优势是:查找速度是 O(1),非常快!

Dictionary空间换时间List 时间换空间

一、什么是"空间换时间" vs "时间换空间"?

✅ 空间换时间

意思是:通过多占用内存空间,来提升运行效率(尤其是查找效率)

例如:

  • 创建一个大型的数组或哈希表,快速定位数据。

  • 为了加快访问速度,牺牲一些内存来保存冗余数据或索引。

    ✅ 时间换空间

    意思是:通过减少内存占用,但牺牲一点运行效率(特别是遍历或查找速度)

    例如:

  • 仅保存最少必要信息,每次都从头计算或查找。

  • 不额外保存中间状态,减少内存消耗。

    二、对比 DictionaryList

    特性 Dictionary<K, V> List<T>
    本质结构 哈希表(Hash Table) 数组(Array)
    查找效率 O(1) 常数时间(哈希定位) O(n) 线性时间(需要遍历整个列表)
    插入效率 快,但哈希冲突时略慢 尾部插入快,插入中间则可能慢
    占用内存 :维护哈希表结构、桶数组等 :仅维护数据本身
    是否保证顺序 不保证插入顺序 保证插入顺序
    使用场景 快速定位、查找、映射(如字典、缓存系统) 顺序存储、小集合、频繁遍历

    三、空间换时间的例子(Dictionary)

    cs 复制代码
    Dictionary<EPageType, PageInfo> uiPages;

    背后的原理是:

  • 系统为每个 Key 计算哈希值(比如将 EPageType.Shop 映射为 13573)。

  • 根据哈希值快速跳转到该数据的内存地址。

  • 这种方式会使用额外的内存结构 (如哈希桶、数组、链表等)来保证快速访问。这就是典型的用空间换时间:提高速度,但牺牲内存。

  • 不需要遍历,直接就能拿到结果。

四、时间换空间的例子(List)

cs 复制代码
List<PageInfo> uiPages;

你只能写:

cs 复制代码
foreach (var page in uiPages)
{
    if (page.Type == EPageType.Shop)
    {
        return page;
    }
}

此时:

  • 查找每个元素需要 O(n) 时间。

  • 不需要额外的哈希结构,节省内存

  • 数据越多,性能下降越明显。

这是时间换空间 :节省内存,但牺牲了执行效率。

五、现实生活类比

场景 类比解释
Dictionary 图书馆:一本书有唯一编号(哈希值),你可以一秒钟定位这本书的位置(快速查找)。为了这个,你得先建一套编号系统,占用一定资源(空间)。
List 堆书桌:书都堆一起了,你要找《C#教程》,只能一本本翻过去,慢,但不需要额外占地(空间节省)。

总结一句话:

类别 定义
Dictionary 是"空间换时间" 用更多的内存结构(如哈希表)来提升数据查找速度,适合频繁查找、修改的数据集合
List 是"时间换空间" 内存占用少,但每次操作如查找需要从头遍历,适合数据量小、频繁顺序处理的场景
PageInfoBaseCtrlBaseView 是什么?作用是什么?
  1. PageInfo:页面的容器(页面元信息)

PageInfo 主要负责绑定以下三件事:

  • 页面类型**EPageType**

  • 控制器组件 BaseCtrl

  • 视图组件 BaseView

  • UI 面板 UIPanel(来自 FairyGUI)

  • gameObject 对象本身

它像是一个页面的数据模型 ,保存了一切有关这个 UI 页面的运行时数据。

  1. BaseCtrl:控制器(逻辑层)

所有页面控制器都应该继承自 BaseCtrl,控制器主要负责:

  • 页面交互逻辑(按钮点击、数据更新等)

  • 与服务器、数据层的交互

  • 接收用户操作、修改视图

控制器内部通常引用它的 PageInfo 和 BaseView。

  1. BaseView:视图(表现层)

视图负责:

  • 加载 UI 元素

  • 控制 UI 的显示/隐藏、动效

  • 响应控制器的命令

视图绑定在 prefab(预制体)上,显示在屏幕上。你可以在 InitRoot() 中绑定 UI 组件:

cs 复制代码
view.m_Panel = showPage.gameObject.GetComponent<UIPanel>();
view.InitRoot(view.m_Panel.ui);

总结整体结构图

cs 复制代码
UIMgr(单例管理器)
├── Dictionary<EPageType, PageInfo> m_Ctrls    // 当前显示页面
├── Dictionary<EPageType, PageInfo> m_Caches   // 缓存页面
│
├── PageInfo(页面信息)
│   ├── EPageType m_PageType
│   ├── BaseCtrl m_Ctrl
│   └── BaseView m_View
│
├── BaseCtrl(控制器)
│   └── 控制逻辑、处理事件、更新视图
│
└── BaseView(视图)
    └── 处理 UI 表现、动画、组件显示
BaseCtrl & BaseView 是什么?职责划分?

在 UI 框架中,通常采用 MVC(Model-View-Controller) 或其变种的架构思想。
BaseCtrlBaseView,分别承担:

类名 角色 职责
BaseCtrl Controller 控制器,负责逻辑处理、按钮响应、事件绑定、数据驱动等
BaseView View 视图,负责界面元素展示、动画、UI 的显示隐藏等视觉内容

它们共同组成了一个页面(PageInfo)的核心功能组合:

cs 复制代码
PageInfo.m_Ctrl => 逻辑处理器
PageInfo.m_View => UI视图渲染器

这种设计符合高内聚、低耦合 的原则。

二、继承结构概览(类图)

cs 复制代码
[BaseCtrl] <--- [ShopCtrl] / [MainCtrl] / [BagCtrl] ...
[BaseView] <--- [ShopView] / [MainView] / [BagView] ...
相关推荐
long31610 分钟前
代理设计模式
java·学习·程序人生·设计模式·代理模式
页面仔Dony17 分钟前
流式数据获取与展示
前端·javascript
张志鹏PHP全栈25 分钟前
postcss-px-to-viewport如何实现单页面使用?
前端
恋猫de小郭26 分钟前
iOS 26 正式版即将发布,Flutter 完成全新 devicectl + lldb 的 Debug JIT 运行支持
android·前端·flutter
俊昭喜喜里37 分钟前
C#和SQL Server Management Studio的连接
服务器·数据库·c#
时光追逐者1 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 51 期(2025年8.18-8.24)
c#·.net·.netcore·.net core
前端进阶者1 小时前
electron-vite_20外部依赖包上线后如何更新
前端·javascript·electron
MThinker1 小时前
14.examples\01-Micropython-Basics\demo_yield.py 加强版
python·学习·智能硬件·micropython·canmv·k230
DanmF--1 小时前
Unity中的特殊文件夹
unity·c#·游戏引擎
月盈缺1 小时前
学习嵌入式的第二十五天——哈希表和内核链表
学习·链表·散列表