zoukankan      html  css  js  c++  java
  • 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类
  • 相关阅读:
    HLS Coding Style: Hardware Efficient C Code
    HLS Coding Style: Arrays and Data Types
    HLS Coding Style: Unsupported C Constructs
    HLS Optimization: Latency V.S. Throughput
    HLS Optimization: Pipeline V.S. Unroll
    HLS Coding Style: Functions and Loops
    HLS Optimization: Latency
    HLS Optimization: Throughput
    hive常见报错
    Neo4j 第三篇:Cypher查询入门
  • 原文地址:https://www.cnblogs.com/raymondking123/p/11369623.html
Copyright © 2011-2022 走看看