初始化只是第一次實例化的時候調用,show和unshow是打開界面和關閉界面的時候,會多次調用
在一個腳本里面show是每一次打開界面的時候需要做的事情,而Init是初始化。
UIMgr里面的數據結構:
為什么我要先從數據結構入手呢?因為程序 = 數據結構 + 算法!!!!!!
UIMgr
(UI 管理器),用于管理游戲中打開、關閉、緩存 UI 頁面,屬于典型的 MVC(Model-View-Controller)架構中的 View 管理層。
List<PageInfo> m_Ctrls
—— 當前正在顯示的頁面列表
類型:
List<PageInfo>
用途:保存當前正在顯示(激活)的頁面。
特性:打開時添加,關閉時移除。
?
List<PageInfo> m_Caches
—— 已經關閉但緩存的頁面
類型:
List<PageInfo>
用途:用作“對象池”,防止 UI 頁面重復加載。
特性:關閉后放入緩存,下次打開優先從緩存中取。
UIMgr(UI管理器)
│
├── PageInfo ?// 每個頁面的信息,包含 Ctrl 和 View
│ ? ├── m_PageType : EPageType ? ? ?// 頁面枚舉
│ ? ├── m_Ctrl : BaseCtrl ? ? ? ? ? // 控制器邏輯
│ ? ├── m_View : BaseView ? ? ? ? ? // UI 視圖
│ ? ├── m_IsOpen : bool ? ? ? ? ? ? // 當前是否打開
│
├── BaseCtrl(控制器)
│ ? ├── Init() / Show() / UnShow() ?// 控制邏輯
│ ? ├── m_PageInfo : PageInfo
│
├── BaseView(UI顯示層)
│ ? ├── Init() / Show() / UnShow() ?// 視覺表現
│ ? ├── m_PageInfo : PageInfo
│ ? ├── m_Panel : UIPanel ? ? ? ? ? // FairyGUI UI根
核心算法流程:
一.打開頁面(OpenPage(EPageType type)
輸入:EPageType(頁面類型枚舉)
算法流程:
1. 檢查是否已經在打開中(m_Ctrls 中查找)
2. 如果緩存中有(m_Caches 中查找),直接復用(緩存指的是“已經關閉但沒有銷毀的頁面實例”,用于復用,避免重復創建,提高性能。)
3. 如果沒有,就創建新頁面:
- 加載資源(從路徑映射表中查路徑)
- 掛載 PageInfo、BaseCtrl、BaseView、UIPanel
- 初始化 Ctrl 與 View
4. 添加到當前頁面列表 m_Ctrls
5. 顯示 Ctrl 和 View
二.關閉頁面 closePage()????????
輸入:可以是 EPageType / PageInfo / BaseCtrl / BaseView
算法流程:
1. 設置頁面狀態為關閉
2. 調用 Ctrl 和 View 的隱藏方法(帶或不帶動畫)
3. 從 m_Ctrls 中移除
4. 添加到 m_Caches(復用池)
5. 廣播事件通知 UI 被關閉三.重新刷新頁面 RefreshCurPage()
1. 取出棧頂頁面(最后一個 m_Ctrls)
2. 關閉它
3. 再打開它(重新加載或從緩存中恢復)四.同時關閉并打開新頁面(CloseAndOpenPage)
1. 先關閉傳入的舊頁面(PageInfo 或 BaseCtrl)
2. 再打開新的頁面
五.查找指定頁面信息
PageInfo GetPageInfo(EPageType type)
遍歷 m_Ctrls,找到匹配頁面類型的 PageInfo
eg:打開背包頁面時,
OpenPage(EPageType.BagPage)
會:
檢查當前 UI 中是否已經有 BagPage
沒有則加載資源,例如
"UI/BagPanel"
實例化組件,掛載腳本
添加到
m_Ctrls
列表展示 UI 視圖
關閉時會反過來處理。
C#基礎語法
語法點 示例 建議學習關鍵詞 類繼承 : Singleton<UIMgr>
泛型繼承、泛型類 構造函數保護 protected override void OnInit()
虛方法、重寫 泛型方法 GetPageCtrl<T>() where T : BaseCtrl
泛型約束 集合操作 List<PageInfo>.Add/Remove
集合遍歷、倒序遍歷 條件判斷 if / else / return
分支語句 循環 for / foreach
倒序刪除元素的寫法 類型轉換 as PlayerEvt
,as T
類型安全轉換 組件操作 AddComponent<>(), GetComponent<>()
Unity / FairyGUI 組件獲取 字典操作 TryGetValue()
Dictionary 用法 日志打印 MyLogger.Info()
打印調試工具類 枚舉 EPageType
枚舉類型使用
基礎語法回顧
什么是泛型?
泛型(Generic)是為類或方法指定“類型參數”的一種機制。可以讓你的代碼更通用、可復用,而且類型安全。
泛型類型示例:public class Box<T> {public T Content;public void Print(){Console.WriteLine(Content);} }
使用方法:
Box<string> box1 = new Box<string>(); box1.Content = "Hello";Box<int> box2 = new Box<int>(); box2.Content = 123;
泛型方法示例:
public class Printer {public void Print<T>(T value){Console.WriteLine(value);} }
調用方式:
Printer p = new Printer(); p.Print("文字"); p.Print(100);
泛型約束:
public T GetPageCtrl<T>(EPageType ePageType) where T : BaseCtrl
這表示
T
必須繼承自 BaseCtrl,否則編譯報錯。這叫泛型約束。
List倒序刪除元素
為什么要倒序刪除?
因為如果你正序刪除,索引會移動,容易漏刪或報錯。如下寫法:
for (int i = list.Count - 1; i >= 0; i--) {if (list[i] == 條件){list.RemoveAt(i);} }
C# override 和 base 的區別
override是重寫父類方法public class Base {public virtual void Speak(){Console.WriteLine("Base speaking");} }public class Child : Base {public override void Speak(){Console.WriteLine("Child speaking");} }
base是調用父類方法public override void Speak() {base.Speak(); // 先調用父類邏輯Console.WriteLine("Child speaking"); }
你可以理解為:
override
: 我要覆蓋父類行為
base
: 我要在子類中繼續使用父類行為
什么是MVC架構
MVC是什么?
MVC 全稱:Model - View - Controller
是一種軟件架構模式,用于分離數據、界面與邏輯,提高可維護性與模塊化。如我的UIMgr里面的MVC架構:
MVC示意圖:
組件 作用 示例類 Model 數據 DataMgr
,ItemData
等View 界面顯示 BaseView
,UIPanel
Controller 控制邏輯 BaseCtrl
Controller 連接用戶輸入與業務邏輯
View 專注展示 UI,不處理邏輯
Model 是純數據(如道具表、角色數據)
UIPanel是什么?
在 FairyGUI 中,UIPanel
是 掛在 GameObject 上的 UI 容器組件,用于將 FGUI 的 UI 顯示在 Unity 場景中。用法說明:
UIPanel panel = go.GetComponent<UIPanel>(); panel.ui = UIPackage.CreateObject("包名", "組件名") as GComponent;
UIPanel.ui
:是一個 FGUI 的GComponent
,就是 UI 根節點每個頁面視圖(BaseView)都掛在這個 panel 上操作
GetComponent<T>()如何用?
Unity 中常用來獲取掛載在 GameObject 上的組件腳本
示例:// 獲取自身掛載的 BoxCollider BoxCollider box = gameObject.GetComponent<BoxCollider>();// 獲取子物體上掛載的 BaseView BaseView view = transform.Find("Child").GetComponent<BaseView>();
常見錯誤:// 錯誤:可能 GameObject 上沒有這個組件 var view = go.GetComponent<BaseView>(); if (view == null) Debug.Log("沒有找到組件!");
?如何異步加載UI?
Unity 中 UI 面板如果太大,推薦異步加載資源文件,以防卡頓。
簡單異步加載方式(基于 Addressables 或 Resources)IEnumerator LoadUI(string path, Action<GameObject> callback) {ResourceRequest request = Resources.LoadAsync<GameObject>(path);yield return request;GameObject obj = GameObject.Instantiate(request.asset as GameObject);callback?.Invoke(obj); }
調用方式:
StartCoroutine(LoadUI("UIPrefab/Bag", (ui) => {// 初始化邏輯 }));
總結
關鍵詞 用途 重點 泛型 類與方法復用,帶類型限制 T
,where T : BaseCtrl
List 倒序刪除 避免刪除錯位 for (i = Count - 1; i--)
override/base 子類覆蓋/調用父類方法 多態 MVC 分離邏輯/界面/數據 架構思想 UIPanel FairyGUI 與 Unity 橋梁 ui = GComponent
GetComponent 獲取組件 類型匹配注意空檢查 異步加載 防止 UI 卡頓 Resources.LoadAsync
,Addressables
緩存?
緩存指的是“已經關閉但沒有銷毀的頁面實例”,用于復用,避免重復創建,提高性能。List<PageInfo> m_Caches;
保留在內存中,放入緩存列表中,等下次打開時直接復用。
原因 說明 🚀 提高性能 打開/關閉頁面頻繁,如果每次都加載資源、實例化,會卡頓、浪費性能 🧠 減少 GC 避免頻繁創建銷毀對象帶來的垃圾回收 ?? 加快響應速度 從緩存中復用 UI 頁面幾乎是瞬間完成的 緩存邏輯是如何運作的?
1.打開頁面時?OpenPage(EPageType type)
:// 先檢查緩存列表 PageInfo pageInfo = GetCachePage(type); if (pageInfo != null) {// 從緩存恢復m_Caches.Remove(pageInfo);m_Ctrls.Add(pageInfo);pageInfo.m_Ctrl.Show();pageInfo.m_View.Show(); }
2.關閉頁面時 Close Page(Page InfopageInfo):
pageInfo.m_IsOpen = false;// 隱藏,但不銷毀 pageInfo.m_Ctrl.UnShow(); pageInfo.m_View.UnShow();// 從顯示列表中移除 m_Ctrls.Remove(pageInfo);// 加入緩存列表 m_Caches.Add(pageInfo);
示意圖:頁面狀態轉換
類似現實中:想象你是個餐館老板:
顧客來了:你先看看有沒有空桌子(緩存)
沒有就新擺一張桌子(創建新的頁面)
顧客走了,你不收桌子,只是讓它暫時閑置,等下個顧客再來用(放入緩存)
緩存的注意點:
UI 頁面狀態要正確重置(避免殘留上次數據)
如果頁面太多,可能要設計LRU緩存策略(如保留最近 3 個頁面,其余銷毀)
緩存列表不能太大,否則內存占用也會增加
緩存是“已關閉但未銷毀的頁面”,用于下次快速復用,提高性能、減少加載。