版權申明:
- 本文原創首發於以下網站:
- 博客園『優夢創客』的空間:https://www.cnblogs.com/raymondking123
- 優夢創客的官方博客:https://91make.top
- 優夢創客的游戲講堂:https://91make.ke.qq.com
- 『優夢創客』的微信公眾號:umaketop
- 您可以自由轉載,但必須加入完整的版權聲明!
萬物起源:Init.cs
- 打開范例場景init.unity,可以發現其場景層級如下:
- 其中唯一重要的就是Global對象上掛在的init.cs腳本,關於其基礎代碼分析,還是建議大家看初見的教程(ghithub有鏈接)
- 在這里只想重點分析大家一定會關心的一個問題:init.cs是如何加載初始界面的
init.cs是如何加載初始界面的:
- 上節課分析了,init.cs首先加載UILoading界面,其加載流程大致是這樣的,先上序列圖,稍后結合序列圖貼代碼分析:
sequenceDiagram Unity->> +Init: StartAsync Init ->> BundleHelper: DownloadBundle() BundleHelper->>EventSystem: Run(EventIdType.LoadingBegin) EventSystem->>LoadingBeginEvent_CreateLoadingUI: Run() LoadingBeginEvent_CreateLoadingUI->>UILoadingFactory: Create() note right of UILoadingFactory: 實例化UILoading預制體,並附加UILoadingComponent(更新並顯示加載進度) Init->>-Unity: StartAsync
- 加載初始界面的幾個步驟如下:
- 調用
EventSystem.Run(EventIdType.LoadingBegin)
引發LoadingBegin事件:
public static class BundleHelper
{
public static async ETTask DownloadBundle()
{
if (Define.IsAsync)
{
try
{
using (BundleDownloaderComponent bundleDownloaderComponent = Game.Scene.AddComponent<BundleDownloaderComponent>())
{
await bundleDownloaderComponent.StartAsync();
Debug.Log("EventIdType.LoadingBegin");
Game.EventSystem.Run(EventIdType.LoadingBegin);
await bundleDownloaderComponent.DownloadAsync();
}
Game.EventSystem.Run(EventIdType.LoadingFinish);
Game.Scene.GetComponent<ResourcesComponent>().LoadOneBundle("StreamingAssets");
ResourcesComponent.AssetBundleManifestObject = (AssetBundleManifest)Game.Scene.GetComponent<ResourcesComponent>().GetAsset("StreamingAssets", "AssetBundleManifest");
}
catch (Exception e)
{
Log.Error(e);
}
}
}
}
- 由於在unity編輯器環境下IsAsync標志被設為false(在VS環境下選中IsAsync成員,右鍵→速覽定義可見),也即異步加載資源才可見loading畫面,所以實際上不會看到loading畫面!
- 第19行為等待異步加載完畢后引發LoadingFinish事件,其流程與LoadingBegin類似,請同學們自行分析!
-
實現LoadingBegin事件處理程序:
[Event(EventIdType.LoadingBegin)] public class LoadingBeginEvent_CreateLoadingUI : AEvent { public override void Run() { UI ui = UILoadingFactory.Create(); Game.Scene.GetComponent<UIComponent>().Add(ui); } }
在這里有需要注意學習定義事件類的方法:
1. 為一個類添加Event標志(參數填具體事件類型)
2. 從AEvent繼承
3. 此時,ET就會自動將該類識別為一個事件處理類(通過反射機制),並在EventSystem.Run
被調用時執行LoadingBeginEvent_CreateLoadingUI事件類的Run方法! -
第六行代碼
UILoadingFactory.Create()
負責創建UILoading界面,下面代碼加了注釋:public static class UILoadingFactory { public static UI Create() { try { // KV是Resources文件夾下存儲的本地預制體資源,主要存儲一些鍵值對數據 // 從KV加載UIType.UILoading預制體,並實例化UI對象: GameObject bundleGameObject = ((GameObject)ResourcesHelper.Load("KV")).Get<GameObject>(UIType.UILoading); GameObject go = UnityEngine.Object.Instantiate(bundleGameObject); go.layer = LayerMask.NameToLayer(LayerNames.UI); // 創建UI這個Entity,並將上面創建的UI對象作為該Entity的圖形表示 UI ui = ComponentFactory.Create<UI, string, GameObject>(UIType.UILoading, go, false); // 添加UILoadingComponent,該組件負責更新loading進度並刷新顯示 ui.AddComponent<UILoadingComponent>(); return ui; } catch (Exception e) { Log.Error(e); return null; } } }
說明:
- UI類是一個Entity類,Entity間接從Component類繼承,但只有Entity類可以附加組件,Component類不行
- Entity和Component的關系實際就是設計模式中的Composite模式
- UI類可以復用,當你要創建一個UI時,在ET框架下只要:
- 添加一個static的UI工廠類,並在其中定義一個static的Create方法,具體實現參照UILoadingFactory
- 為該工廠添加一個新的UI組件(從Component類繼承),並實現該組件的事件系統(見下文) -
實現UILoadingComponent並實現該組件的事件系統:
- UILoading組件
public class UILoadingComponent : Component { public Text text; }
- UILoading事件系統:
[ObjectSystem] public class UiLoadingComponentAwakeSystem : AwakeSystem<UILoadingComponent> { public override void Awake(UILoadingComponent self) { self.text = self.GetParent<UI>().GameObject.Get<GameObject>("Text").GetComponent<Text>(); } } [ObjectSystem] public class UiLoadingComponentStartSystem : StartSystem<UILoadingComponent> { public override void Start(UILoadingComponent self) { StartAsync(self).Coroutine(); } public async ETVoid StartAsync(UILoadingComponent self) { TimerComponent timerComponent = Game.Scene.GetComponent<TimerComponent>(); long instanceId = self.InstanceId; while (true) { await timerComponent.WaitAsync(1000); if (self.InstanceId != instanceId) { return; } BundleDownloaderComponent bundleDownloaderComponent = Game.Scene.GetComponent<BundleDownloaderComponent>(); if (bundleDownloaderComponent == null) { continue; } self.text.text = $"{bundleDownloaderComponent.Progress}%"; } } }
事件類的定義:
1. 添加[ObjectSystem]標志
2. 繼承自對應的XxxSystem類,並實現基類的虛方法
- 事件類與Unity中含義類似,請自行參閱源碼學習
總結:
- 通過對UILoading的學習,我們已經接觸了ET的一個完整的ECS對象:
- E:Entity,對應UI類
- C:Component,對應UILoadingComponent類
- S:System,
對應UiLoadingComponentAwakeSystem和
UiLoadingComponentStartSystem類