1. 前言

ET算是我刚接触客户端时最早知道的框架,ET我最初眼馋的还是他的双端功能。包揽前后端的功能,这个很有吸引力。但是那时候对我来说这框架太复杂了,没法看。

这两天又来看看,曾经很多不懂的地方现在都能看懂了,看了之后发现这框架还是不适合我,原因总结里再说,在这里做一下记录。

首先说一下很多新手看这个框架的疑惑点,再说一下ET最有特色的几个功能:ETTask、事件系统、双端开发。

2. 新手常见问题

这些问题是我以一个新手的视角来看的。也是我最初接触这个框架以来有疑惑的地方。

2.1 目录结构问题

这里以7.2的目录为例。ET6就不用看了,就是闹着玩儿。

代码位置

主要关心以下三个目录,其中Share文件夹下主要是一些工具和分析器,分析器作用是规范C#的代码规范,编码时做出提示,不需过多关注。

代码都在Unity下,DotNet目录下有csproj,引用的Unity/Assets/Scripts/Codes里的文件,这样服务端开发就只关心Dotnet目录下的代码就行,再次强调,代码文件在Unity目录下,DotNet里相当于是软链接。

这样代码统一放到Unity工程里,双端开发也方便(ps: 实际我还是觉得客户端和服务端分开更好)

Unity目录

这个就是Unity工程目录,代码在Assets\Scripts 下,这下面又有很多目录,
其中Empty是占位用的,占着几个程序集(ps: 具体啥作用没细看,估计编译需要)
Loader是程序入口,加载程序集
Core、Editor、ThirdParty见名知意
Codes就是我们写程序的目录,DotNet也是链接到这个目录的。

其中目录如下,四个目录,Model ModelView Hotfix HotfixView,这样划分是为了逻辑和数据分离。 Model只有数据。hotfix只有逻辑。其中 Model view和HotfixView,只有客户端用,用于表现层。

这四个目录的每个目录下分别有Client Server Share文件夹,Client写客户端代码,Server写服务端代码,Share写两端共用的代码,这样服务端也有客户端代码,可以做AI机器人。

2.2 程序集问题

看了目录划分后对于纯新手来说又有疑惑了,程序集是什么?作用是什么?

这还真是我一开始的疑惑,程序集可以理解为包,一个程序集可以编译成.dll给别的程序集使用。

那为什么这么分程序集呢?

这里你得明白dll或者说so文件的作用,可以动态链接。这样就能实现热重载的功能。
同时能提高编引速度。比如有ABC三个程序集。 A依赖B,B依赖C,这时候如果B修改了,只有AB会重新编译。如果只有A修改了,则只有A会重新编译。

这里你顺便说一个程序域的概念,看源码的时候能看到AppDomain,一个进程可以有多个域,一个域可以加载多个程序集。区域相当于提供一个独立的运行环境。

3. ETTask

为什么不用原生Task呢,因为Task是多线程的,起一个Task会在子线程运行。
ETTask实现了单线程的Task,其他类似的库还有UniTask

使用多线程实际没什么问题,但是要保证另外的线程逻辑是线程安全的,然而有时候你觉得是线程安全的,可能会有各种bug,这种bug就不好找。还要处理数据线程数据同步问题。
而unity主程序本身是单线程的,加上多线程强行增加复杂度,使用单线程异步可以避免很多问题。
Unity对跨线程的处理都是把委托投递到一个队列,主线程Update()不停从队列中取出委托执行

那为什么不用Corotine呢?
Corotine必须在mono脚本下执行,而且效率貌似也不如Task异步。

4. 事件系统与标签

这个可以说是ET的核心了,不过不能被名字欺骗了,这个事件系统不只是其他游戏框架中用到的那种事件,这里面还管理所有的标签类型。

ET的一个特色就是对标签的大量使用,这也是对新手很不友好的地方,也大大提高了代码的阅读难度。

代码就是Core/Module/EventSystem/EventSystem.cs,目前看其中主要实现了对不同标签类型的管理,这里只说两个,常见的事件系统和SystemComponent机制

4.1 常见的事件系统

使用

就是一般游戏框架中用到的那种事件系统,ET的用法比较特殊,如下图所示,对事件的注册不是显式调用register,而是写一个类,打上Event标签,这个类要继承AEvent,AppStartInitFinish就是事件参数,也是通过这个区分事件的,下图中这个事件对应两个处理方法,触发事件的时候会调用这个类的Run()函数

原理

简单来说,就是EventSystem 启动时找到所有打上Event标签的类统一管理,调用Publish发布事件时,根据事件参数找到对应的事件类。依次调用每个事件类的Run方法。

下面是截代码的图分析:

  1. 启动之前会注册事件,通过Attribute的方式

  2. 在Add方法中,会找到所有BaseAttribute标签的类型,所有自定义的标签都是继承自这个标签

  3. 再在其中找到所有event标签的类型,加入allEvents统一管理

  4. 同理处理Invoke标签的类型。

  5. 事件触发,就是从allEvents中找到T类型对应的事件。一个事件可能有多个EventInfo,也就是多个处理响应,依次进行调用。比如下面第2张图中的例子。其中加了一个scene的概念,相当于就是对事件又进行分层,只发到对应的层。

  6. 上图中对时间的使用。添加Event标签才能注册到事件系统里。继承AEvent是因为事件系统会调用它的Handle方法,Handle实际调用的Run,Run()就是自己要实现的对事件的处理。

4.2 SystemComponent机制

这个也是用上面的EventSystem实现的。

  1. 还是如上面的例子在Add()中找到所有ObjectSystem标签并用typeSystems统一管理。

  2. 比如Awake的系统。调用EventSystem的Awake后,所有实现该接口的系统都会调用

  3. 以MoveComponent为例,它的MoveComponentSystem里实现了AwakeSystem,在这里打断点调试一下,可以看到调用栈如图2,总之就是在添加MoveComponent这个组件时,调用了EventSystem.Instance.Awake(component),从而调用了所有继承AwakeSystem<MoveComponent>的类型的Run。

4.3 单独声明一下

因为没用ET写过项目,只是看了下代码,可能对这EventSystem理解有偏差,不过应该没啥大问题

5. 热重载

下面两个图就能知道热重载的大概逻辑。
按R调用CodeLoder的LoadHotfix()方法。
这个方法里重新加载Hotfix的dll。
可见热重载之前要先手动编译一下Hotfix dll,再按R。

5. 双端开发

实际就是客户端代码写在Client 的目录下,服务端写在Server目录下,实际上客户端和服务端的交互还是通过RPC。

这么一看,双端开发就是能在一个编辑器下开发,我反而觉得很不方便。因为每个目录下都划分client 和server,看着太乱了,不如开两个项目。

关于共用代码,这种需求感觉并不是那么需要,使用的一样的代码直接复制就行了。

总结

最后再声明一下,这个框架我还没有弄得特别透彻。因为现在大致看懂了整体架构之后,觉得不太适合我。也就没有了继续深入的想法。以上只是看源码,demo的一些收获,有错误的地方欢迎指正。

再说几句,我的个人想法,仅代表个人意见。我觉得这样整的过于繁琐了,无论是目录的划分,还是所谓的独特的ECS机制。

也没有体会到这种System-Component的好处,只觉得代码过于散乱,可能是没有实际使用ET开发过,只是看了看例子吧。

双端开发也没有当初一无所知时所认为的那么神秘,还是客户端代码和服务端代码分开了,反而代码混合在一起显得更乱了,不如分开。而且服务端定义的zone、scene这一套,我现在还没完全理解这些概念,估计应该是区、服和进程的区分,相当于得完全在这一套逻辑下使用,没用过所以也不知道方不方便拓展。

事件系统和标签的使用也感觉不适应,当然熟悉了这种开发方式后也能用,但是就和JEngine中使用的订阅模式的事件系统一样,可以但没有必要,还是传统观察者模式的事件系统更好用一些,其他也没有什么效率上的优化。

热重载是有用,但对于快速开发又不是很必须。对于服务端来说热重载就应该更不需要了吧,就我有限的服务端开发经历,之前写的寻路服务,需要构建全地图的障碍和navmesh信息,重启虽然不是立刻就启动了,但用的时间也不是不能忍受。

当然,这框架的实现还是挺独特的,可能是我还体会不到这框架的好处,虽然现在暂时pass了,以后还会继续关注,可能以后就用到了呢。

【OpenSourceC#】ET框架相关推荐

  1. ssh(Struts+spring+Hibernate)三大框架整合-简述

    ssh(Struts+spring+Hibernate)三大框架配合使用来开发项目,是目前javaee最流行的开发方式,必须掌握: 注意: 为了稳健起见,每加入一个框架,我们就需要测试一下,必须通过才 ...

  2. Gin 框架学习笔记(03)— 输出响应与渲染

    在 Gin 框架中,对 HTTP 请求可以很方便有多种不同形式的响应.比如响应为 JSON . XML 或者是 HTML 等. ​ Context 的以下方法在 Gin 框架中把内容序列化为不同类型写 ...

  3. Gin 框架学习笔记(02)— 参数自动绑定到结构体

    参数绑定模型可以将请求体自动绑定到结构体中,目前支持绑定的请求类型有 JSON .XML .YAML 和标准表单 form数据 foo=bar&boo=baz 等.换句话说,只要定义好结构体, ...

  4. QT学习之状态机框架

    状态机框架 创建状态机

  5. 【Spring】框架简介

    [Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...

  6. 开源自动化机器学习框架

    20211101 在 Airbnb 使用机器学习预测房源的价格 https://blog.csdn.net/weixin_33735077/article/details/87976278?spm=1 ...

  7. Keras框架下的保存模型和加载模型

    在Keras框架下训练深度学习模型时,一般思路是在训练环境下训练出模型,然后拿训练好的模型(即保存模型相应信息的文件)到生产环境下去部署.在训练过程中我们可能会遇到以下情况: 需要运行很长时间的程序在 ...

  8. Adam那么棒,为什么还对SGD念念不忘 (1) —— 一个框架看懂优化算法

    机器学习界有一群炼丹师,他们每天的日常是: 拿来药材(数据),架起八卦炉(模型),点着六味真火(优化算法),就摇着蒲扇等着丹药出炉了. 不过,当过厨子的都知道,同样的食材,同样的菜谱,但火候不一样了, ...

  9. 一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

    Adam那么棒,为什么还对SGD念念不忘 (1) -- 一个框架看懂优化算法 机器学习界有一群炼丹师,他们每天的日常是: 拿来药材(数据),架起八卦炉(模型),点着六味真火(优化算法),就摇着蒲扇等着 ...

最新文章

  1. java 解析注解_Java知识点总结(注解-解析注解)
  2. MyBatis 的这些坑你有踩过吗?
  3. Insertion Sort List(单链表插入排序)
  4. sqlite3打开中文路径数据文件失败解决方法
  5. [SpringSecurity]HelloWorld入门案例
  6. Jquery遮罩插件,想罩哪就罩哪!
  7. Jquery插件 bootstrap-datepicker 日期拾取器
  8. Atitit postgresql data type 数据类型与mysql对应表 数据库常用数据类型 Postgre Mysql 整数 intgreter Int 小数 numeric FL
  9. linux 小度 驱动_分享:bananian1508成功编译小度Wifi mt7601u驱动。
  10. loading等待载入正在加载的动画GIF图片圆形图标
  11. php 获取pdf中的图片,使用PHP从PDF中提取图像
  12. nm命令 查看符号文件
  13. eNSP第五篇扩展1:vrrp over nqa,nqa探测,双重vrrp监听方法1,vrrp与nqa结合
  14. centos7上部署php7遇到的坑
  15. POJ1753(枚举)
  16. 干趴网络协议:IS-IS 特性
  17. 浏览器创建render 树_如何为浏览器创建出色的游戏
  18. 2022.09.01 最新配置maven阿里云仓库配置
  19. nginx根据ip限流和突发流量配置解释
  20. systemverilog中的时间单位和时间精度

热门文章

  1. 关于iPhone6/iPhone6 Plus适配的问题
  2. Show()跟ShowDialog()的区别
  3. show**介绍**
  4. 能让iPhone工作Android系统的手机壳
  5. 算力革命有了新选择,联想发布全新本地化服务器品牌“联想问天”,与联想ThinkSystem双品牌驱动
  6. [教程] openmp/sa-mp联机服务器开发 零基础入门
  7. 手机计算机错误改名字,电脑和手机qq怎么改名字?
  8. 单元格内多个姓名拆分成一列_Excel如何将姓名分拆成姓和名两列(使用数据分列完成)...
  9. 红米Note3全网通解锁BL以及刷TWRP
  10. 蓝牙BLE OTA 升级 CRC校验