Unity進階之ET網路遊戲開發框架 02-ET的客戶端啟動流程分析

  • 2019 年 10 月 3 日
  • 筆記

版權申明:

  • 本文原創首發於以下網站:
  1. 部落格園『優夢創客』的空間:https://www.cnblogs.com/raymondking123
  2. 優夢創客的官方部落格:https://91make.top
  3. 優夢創客的遊戲講堂:https://91make.ke.qq.com
  4. 『優夢創客』的微信公眾號: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
  • 載入初始介面的幾個步驟如下:
  1. 調用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類似,請同學們自行分析!
  1. 實現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方法!

  2. 第六行程式碼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類繼承),並實現該組件的事件系統(見下文)

  3. 實現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類