版权申明:

  • 本文原创首发于以下网站:
  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类

Unity进阶之ET网络游戏开发框架 02-ET的客户端启动流程分析的更多相关文章

  1. Unity进阶之ET网络游戏开发框架 01-下载、运行

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  2. Unity进阶之ET网络游戏开发框架 04-资源打包

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  3. Unity进阶之ET网络游戏开发框架 05-搭建自己的第一个Scene

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  4. Unity进阶之ET网络游戏开发框架 08-深入登录成功消息

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  5. Unity进阶之ET网络游戏开发框架 03-Hotfix层启动

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  6. Unity进阶之ET网络游戏开发框架 06-游客登录

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  7. Unity进阶之ET网络游戏开发框架 07-修正游客登录的异步BUG

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  8. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  9. 【网络爬虫入门02】HTTP客户端库Requests的基本原理与基础应用

    [网络爬虫入门02]HTTP客户端库Requests的基本原理与基础应用 广东职业技术学院  欧浩源 1.引言 实现网络爬虫的第一步就是要建立网络连接并向服务器或网页等网络资源发起请求.urllib是 ...

随机推荐

  1. 十代雅阁广东车友群,雅阁广州车友群,深圳雅阁车友群,雅阁微信群、雅阁车友群、十代雅阁交流微信QQ群

    最近一直在关注第十代雅阁,不论是普通汽油版本还是油电混动版本都很不错,在网上看到很多评测文章和视频 后续都会整理发布到微信群中. 由于论坛发帖,博客发文都不是很方便,为了及时沟通,先创建了微信群,方便 ...

  2. python数据库查询转dataframe

    1. 场景描述 python环境下需要从greenplum/postgresql中,获取算法执行的数据,但是从数据库中查询出来是数组格式的,算法无法使用,需要转换为dataframe格式. 2. 解决 ...

  3. Bzoj 2563: 阿狸和桃子的游戏 题解

    2563: 阿狸和桃子的游戏 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 970  Solved: 695[Submit][Status][Discu ...

  4. TCP/IP协议-网络编程

    本文转载自公众号“呆呆熊一点通”,作者:呆呆 开篇语 前两年, 就买了<TCP/IP网络编程>这本书, 由于自身基础薄弱, 只是走马观花翻阅了几张. 后来工作了这些年, 越来越感到瓶颈期已 ...

  5. python 之 并发编程(守护进程、互斥锁、IPC通信机制)

    9.5 守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就立即终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic process ...

  6. 洛谷P2880 [USACO07JAN]平衡的阵容Balanced Lineup 题解

    题目链接: https://www.luogu.org/problemnew/show/P2880 分析: ST表实现即可,一个最大值数组和最小值数组同时维护 代码: #include<cstd ...

  7. docker学习笔记-简介

    零.什么是Docker 是一个基于GO语言开发的开源 应用容器: 开发者可以打包应用和相关包,到一个 轻量级 . 可移植 的 容器 中,并且可以发布到 任何机器 ,实现 虚拟化: 完全使用 沙箱机制, ...

  8. [leetcode] 21. Merge Two Sorted Lists (Easy)

    合并链表 Runtime: 4 ms, faster than 100.00% of C++ online submissions for Merge Two Sorted Lists. class ...

  9. 织梦(dede)底层模板概念、常用底层模板字段

    织梦(dede)底层模板概念.常用底层模板字段 一.底层模板的概念以及调用方式: 1. 什么是底层模板? 底层模板不是一个模板! 他就是在实际页面当中所要显示的具体内容: 2. 底层模板的应用: 调用 ...

  10. Samba:打造企业级授权文件共享服务器

    写在前面的话 先来说说故事背景:公司内部文件服务器的解决方案其实很多,对于中小型互联网公司,大多的在这一块的选型还是 FTP,或者 VSFTP,但是个人实在是对那个东西喜欢不起来,于是就选择了配置相对 ...