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

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

版權申明:

  • 本文原創首發於以下網站:
  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類