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類


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM