【ET框架】基础及实践难题解答

【ET框架】基础及实践难题解答客户端点击登录按钮 含账号 密码 服务器 IP 地址 触发 login 方法 创建一个 session 绑定服务器二进制版本 IP 地址 将客户端页面的信息赋值给 C2R Login 实例 使用 session 发送

大家好,欢迎来到IT知识分享网。

6.0 疑问可以直接看 13.ET基础问题总结


ET


1、ET 框架基础

(1).前后端通讯流程Demo

整个Demo的流程围绕这张图的顺序来执行
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述



(2).登录系统Demo总结

ET框架的登录流程: 客户端点击登录按钮(含账号、密码、服务器IP地址)触发login方法,创建一个session(绑定服务器二进制版本IP地址),将客户端页面的信息赋值给C2R_Login实例,使用session发送请求,LoginHandler会拦截C2R_Login类型的请求并且返回R2C_Login类型的响应,之后关闭该session,因为此处session主要请求Realm网关负载均衡用来获取网关的key和IP,得到了之后就可以再次创建session直连网关来登录


(3).ESC编程原则

ESC相关概念

要严格遵循要求,不同文件放置不同的内容
更新之后组件接口出 IAwake,实体需要实现IAwake、IUpdate、IDestroy
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



包括根据ET框架中ESC组件思想的处理方式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了方便拓展,可以采用switch、case的方式来实现:
在这里插入图片描述





(4).组件生命周期

在这里插入图片描述
在这里插入图片描述


(5).Scene层级树

①.什么是Scene

在这里插入图片描述

②.客户端Scene的层级关系

在这里插入图片描述
第4点中的Computer实体就是挂载在Zonescene下方

③.服务器端Scene的层级关系

在这里插入图片描述
比如ET框架开发了一款网游,它的客户端只有一个ZoneScene,因此只有一个进程。而服务器可以拥有多个进程,每个进程都是一个ZoneScene,比如地图和位置都可以是不同的ZoneScene。这意味着ET框架中的服务器实际上是一个单线程多进程的服务器。

④.服务器机器人Scene的层级关系

在这里插入图片描述

(6).关于Scene的一些疑问

①.如何创建新的ZoneScene

服务器当中创建ZoneScene的方法就是在SceneType里面添加ZoneScene的名称和对应ID,之后在SceneFactory中添加case newZoneScene和对应的组件并编译。之后再StartSceneConfig文件中按表格填入newZoneScene的对应数据,启动win_startExcelExport,重新编译之后newZoneScene就成功创建了。

②.self.ZoneScene()

在客户端代码中,可以使用self.ZoneScene()来获取客户端所连接的服务器ZoneScene的实例。但是,在服务器端代码中,self.ZoneScene()这样的代码是无法使用的,因为服务器端没有self这个对象。
self.DomainScene()通常用来获取客户端所连接的服务器DomainScene的实例


(7).Excel配置工具使用

在这里插入图片描述
上图中cs文件的类属性由excel文件中配置来决定,我们根据不同需求在不同端展现不同类; ID不可重复
同样的我们可以在Model\Generate\ConfigPartial\StartSceneConfig.cs中定义配置类的一些逻辑函数 比如根据ID查找、根据身高查找
在这里插入图片描述


配置完excel之后,运行win_startExcelExport; 类属性就会根据excel配置来刷新,excel中的json文件使用vscode打开,能够看到json格式的类属性 

(8).ET框架事件系统

首先需要在ET-release6.0\Unity\Codes\Model\Demo\EventType.cs当中设计你的结构体
说明: Model本身不是显示层无法调用Unity相关API,但是可以使用vector3因为我们在ThirdParty中的UnityEngine中定义了相关内容,Vector3本身是纯数字计算函数,所以依然可以在Model层中使用。前提是定义好命名空间
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上图代码中就会在登陆界面初始化之后,调用InstallComputer结构体定义好的事件函数





(9).ETTask异步编程

同步: 当代码执行到同步操作的时候,会阻塞在这里等待同步操作执行完才能继续执行后面的代码
异步: 调用了异步操作之后,代码就可以直接继续往后执行了,等异步操作做完会把结果在发送回来;在 C# 中,可以使用asyncawait关键字来实现异步编程。例如,可以定义一个 async 方法来执行异步操作,并在调用该方法时使用 await 关键字等待操作完成。
await 并不是同步,它知识暂停了当前方法的执行,但不会阻塞整个线程
简单说await是放在async标记的方法里面用的,执行到这里的时候必须等待await语句执行完,不过不会干扰主线程,因为await也是被包裹在async方法里面的


调用一个async函数时,你可以选择使用await等待它的结果,也可以不使用await。如果你不使用await,那么async函数会立即返回一个Promise对象,你可以通过在这个Promise对象上调用.then方法来获取它的结果。但是,如果你在一个async函数内部调用另一个async函数,并且希望在继续执行之前等待它的结果,那么你应该使用await1


(10).ProtoBuf通讯消息(非Google开源C++版本)

在这里插入图片描述
在这里插入图片描述
使用VSCODE打开Porto文件夹
注: Proto文件中定义的属性所对应的数字不能相同
在这里插入图片描述
Unity\Codes\Model\Generate\Message\OuterMessage.cs文件中会产生与proto文件对应的类
在这里插入图片描述





(11).网络通讯消息编写


(12).Actor模型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


(13).ET服务器与客户端之间登录流程

在这里插入图片描述

(14).服务器的编写流程

①.SceneType添加服务器

在这里插入图片描述

②.Server.Model中添加组件和所需变量

在这里插入图片描述

③.Server.Hotfix中添加组件所需的函数和周期函数

在这里插入图片描述

④.StartSceneConfig中配置服务器信息

在这里插入图片描述
内网端口在:StartProcessConfig@s文件夹中,编写完之后运行win_startExcelExport

⑤.编写账号服务器与登录中心服务器交互信息

由于登陆服务器主要是服务器之间传输信息,所以我们在InnerMessage中编写 注释也很重要
在这里插入图片描述

⑥.在SceneFactory里面补上登陆服务器的组件

在这里插入图片描述

⑦.登录模块中发送讯息给登陆中心服务器做判断

在这里插入图片描述
在登录模块里面使用ActorMessage来发送彼此的信息。第一句代码应该是拿到当前服务器配置信息,然后拿到服务器的ID,拿着账号ID发送一个请求给这个服务器ID并且拿到响应,判断下响应中的错误码是否正确

⑧.编写登录中心服务器与网关服务器交互信息

在这里插入图片描述

⑨.Server.Hotfix编写登录信息拦截方法

在这里插入图片描述
其中能够看到,如果用户登录的账户可以在登陆中心服务器中找到,那说明该账号已经在线。此时我们需要从登陆中心服务器发送消息给网关服务器来将该账号踢下线。因此我们发送了L2G的信息,再去编写一个拦截方法来回应。

⑩.编写G2L的逻辑处理回应

在这里插入图片描述
在这里我们直接找到网关重所在角色,直接将其移除“在线的游戏角色”字典,让该角色掉线,然后关闭链接简单处理。


(15).UI界面入门


(16).公共UI的创建与使用


(17).登录模块Realm、Gate流程

在这里插入图片描述
登录流程: 用户登录成功之后选择角色,当用户点击确定。客户端发送一条请求向Account服务器请求Realm负载均衡服务器请求一个Gate的key,之后Account需要向Realm服务器发送请求得到这个key。为什么客户端不能直接向Realm服务器请求这个gatekey呢?

客户端不能直接向Realm服务器请求GateKey的原因可能是出于安全和验证的考虑。当客户端向Account服务器请求GateKey时,Account服务器可以验证客户端的身份和权限,然后再向Realm服务器请求GateKey。这样,Realm服务器只需要处理来自已验证的Account服务器的请求,而不需要处理来自所有客户端的请求,这可以减少Realm服务器的负载,并提高系统的安全性。此外,这种设计也有助于分离关注点,使得每个服务器只需要关注其特定的任务。这是一种常见的设计模式,被广泛应用于许多大型分布式系统中。

(18).3D场景创建与服务端导航数据生成

①:将地表预设物设为静态

在这里插入图片描述

②:打开Navigation导航网格面板

在这里插入图片描述

③:直接Bake

在这里插入图片描述

④:选择想导出的位置,将导航数据导出

在这里插入图片描述

⑤:将导出的导航数据拖入场景

在这里插入图片描述

⑥:将数据中的子物体标签(Tag)设置为NavMesh

在这里插入图片描述

⑦:Tools使用工具生成网格数据,数据将显示在下方路径

在这里插入图片描述在这里插入图片描述

⑧:双击RecastDemo.exe , 打开后在右侧面板选择第一个,Map1.obj

在这里插入图片描述
在这里插入图片描述

⑨:此时就能够看到面板,右侧移到下方选择Build,之后选择Save

在这里插入图片描述在这里插入图片描述

⑩:之后bin文件发生变动,我们将其Copy到图二路径。删除掉Map1,将bin文件改名为Map1

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

①:打开Unity,将所有地表预设物Layer设置为Map,没有可以自行添加。与此同时隐藏我们之前放在场景中的网格数据。

在这里插入图片描述
这边取消勾选就行
在这里插入图片描述

②:打开服务器sever.app,F5 unity,启动init场景,就可以了!

在这里插入图片描述


(19).角色跟随摄像机功能

①:Package Manager中安装Cinemachine插件

在这里插入图片描述

②:Asset/ModelView 与 HotfixView 中新增引用并且Apply

在这里插入图片描述

③:打开init场景查看MainCamera的Tag是否是MainCamera,这决定代码中的名字

在这里插入图片描述

④:Unity.ModelView定义相机跟随组件

在这里插入图片描述

⑤:Unity.HotfixView编写System

AwakeSystem直接调用self.Awake();
DestroySystem销毁组件
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述
Mouse ScrollWheel
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



(20).AOI视野模块

①:什么是AOI?

Area Of Interest
一个游戏对象实体(Unit)在游戏地图场景当中的所能看到和接触到的区域范围,这个区域范围可以覆盖整个游戏地图场累,也可以被缩小至游戏对象实体周围几米的区域范围内。

②:为什么需要AOI视野管理?

(21).AOI视野模块源码解析

①:何时挂载AOI相关组件

在这里插入图片描述在这里插入图片描述

②:挂载后Awake函数内容

在这里插入图片描述
在这里插入图片描述
左手坐标系(Unity使用): 伸开我们的左手, 掌心向外, 大拇指与食指成90度, 中指、无名指和小指弯曲, 大拇指指向的方向就是X轴正方向, 食指指向的方向就是Y轴正方向, 中指、无名指和小指指向的方向就是Z轴正方向。
在这里插入图片描述
在这里插入图片描述



(22).关于不同消息处理抽象类的使用

AMHandler: 这是一个基础的消息处理类,它处理的是普通的消息(Msg)。这些消息通常是无需响应的。

AMRpcHandler: 这个类用于处理远程过程调用(RPC)的请求和响应。这些消息通常需要响应。

AMActorRpcHandler: 这个类用于处理发送给Actor的RPC消息。这些消息通常是服务器之间的通信。

AMActorLocationRpcHandler: 这个类用于处理发送给特定位置的Actor的RPC消息。这些消息通常是在服务器之间进行特定位置通信时使用。

actor实际上就是服务器的信箱,服务器可以自己根据任务进度去信箱里面“取件”然后处理,其实类似于java的rabbitmq中间件,微服务做完一个任务会自己去取下一个任务。而且actor主要是针对服务器之间的通信。

⭕ET基础问题总结

1:我的组件没有ID这个属性,从何而来?

在ET框架中,每一个实体(Entity)都有一个唯一的ID。这个ID是在实体被创建时自动分配的。所以,当你看到account.Id这样的代码时,这个Id实际上是来自于Entity类的

2:ATimer类与计时器类与run方法的关系

ATimer是一个抽象类,它定义了一个计时器的基本行为。如果你定义了一个计时器类,但是没有创建计时器,那么这个类中的Run方法将永远不会被执行

3:组件与客户端关联吗?每个玩家的客户端上都有自己的组件吗?

组件通常挂载在服务器上,并由服务器管理。它们不会与任何特定的会话或客户端状态相关联。相反,它们通常以独立的方式运行,并根据需要为所有玩家提供服务。例如,登录检查组件可能会在每个玩家登录时运行,以验证其凭据并授予访问权限。尽管每个玩家都可以使用该组件,但该组件本身仍然独立于任何特定的玩家或会话。

4:ET框架服务器是单线程,多个玩家同时请求同一个组件的时候如何执行?

在ET框架中,登录组件是用来处理用户登录请求的。当多个玩家同时点击登录时,服务器会依次处理每个登录请求。由于ET框架是单线程但可异步的,所以服务器可以在处理一个登录请求的同时,异步等待其他登录请求的结果。

5:只要不使用async就一定不会异步执行吗?

服务器是否异步并不仅仅取决于你写的组件方法是否是async方法。它还取决于服务器所使用的框架和并发模型。在ET框架中,服务器本身就支持异步操作,即使你没有在组件方法中使用async关键字。

6:awake生命周期函数的执行时间点

8:domainScene、zoneScene究竟是什么?

ET框架中,domainScene是属于服务器server文件夹中使用的,它代表当请求所属服务器,因为调用服务器方法都会传进来一个Scene类型,这个Scene就是该玩家所属服务器的信息。domainscene表示所属区服。而客户端unity文件夹中有一个zonescene,它表示当前场景所存在的信息。客户端中可以调用domainscene,但是服务器无法调用zonescene

9:Excel配置中127.0.0.1:10005地址和端口究竟是啥?

127.0.0.1:10005是服务器的IP地址和端口号。127.0.0.1是一个特殊的IP地址,它代表本机,也就是你的电脑。端口号10005是服务器监听的端口;如果你想让前端代码连接到其他电脑上的服务器,你需要将IP地址改为那台电脑的IP地址。例如,如果服务器运行在IP地址为192.168.1.100的电脑上,那么你应该将前端代码中的服务器地址改为192.168.1.100:10005。

10:为什么游戏物体添加了多个组件,只返回其中一个,返回内容同样可以包含其他组件

11:为什么我Unity的 文字是 TMP不能显示,或者显示为中文乱码

解决文章

12:Call、CallActor、CallLocationActor的区别?

13:publishasync出现:event error: xxxxxxxxxxxxxxxxxxx

出现类似错误因为你的消息拦截类,使用的是AEvent<> 而不是 AEventAsync<>

14:Unity中的代码该如何Debug?

❗❗❗15:IDeserialize 和 ISerializeToEntity 之间有什么作用?

两者都能进行序列化与反序列化,ISerializeToEntity 的组件一般是 IDeserialize 的子组件
ISerializeToEntity 在使用 AddComponent 或 Addchild 的时候,源码中 [BsonElement("C")] 自动序列化到数据库中 名为"C"
IDeserialize 在使用 AddComponent 或 Addchild 的时候,源码中 [BsonElement("Children")] 自动序列化到数据库中 名为"Children"

// 反序列化 public class BagComponentDeserializeSystem : DeserializeSystem<BagComponent> { 
                   public override void Deserialize(BagComponent self) { 
                   foreach (Entity entity in self.Children.Values) { 
                   self.AddContainer(entity as Item); } } } 

❗❗❗16:如何将消息发给自定义服务器?

1.首先在Proto中定义好消息内容,其中的注释为自定义的消息接口名称
IActor*Request
在这里插入图片描述
2.在D:\GameWork\ET-EUI-main\Unity\Codes\Model\Module\Actor创建你的消息接口声明,名字定义为:IActorRankInfoMessage.cs
声明内容如下,第一条为不需要回复的消息接口定义,后两条为request和response
在客户端声明之后我们同时引用Add links到服务端的model\Module\Actor中
在这里插入图片描述
3.在D:\GameWork\ET-EUI-main\Unity\Codes\Model\Generate\Message\OuterMessage.cs文件中alt+enter一下你的接口;服务端与客户端都需要这个操作
在这里插入图片描述
4.最后开始修改Server\Hotfix\Demo\Session\SessionStreamDispatcherServerOuter.csDispatchAsync方法,在这个方法中,将接口进行判断,不同的接口做不同的处理,实际上也就是不同的接口表示不同服务器之间的消息通讯
在这里插入图片描述
6.这种方法的拦截类采用 AMActorRpcHandler
同时需要在D:\GameWork\ET-EUI-main\Server\Hotfix\Demo\Scene\SceneFactory.cs中添加case,以此判定不同服务器需要添加的组件











❗❗❗17:如何将消息发给服务器?

在ET框架中,服务器之间的通信主要依赖于Actor消息机制。实体对象只需要挂上MailBoxComponent消息组件,这个实体对象就成了一个Actor,任何服务器只需要知道这个实体对象的id即可向其发送消息,完全不用关心实体对象在哪个服务器上(如UnitId)。原理就是ET框架提供了一个位置服务器,所有挂在MailBoxComponent的实体对象都会将自己的id跟位置注册到这个位置服务器上,其他服务器向这个实体对象发送消息的时候如果不知道这个实体对象在哪个服务器上,会先去位置服务器查询,查询到位置再进行发送。

至于你提到的两种方法CallLocationActor和CallActor,它们都是用来发送Actor消息的。不同的是,CallLocationActor的第一个参数是目标实体的UnitId(所以这个消息是发给玩家这个实体),而CallActor的第一个参数是目标实体的InstanceId1。这两个参数都是用来标识目标实体的,但是它们在不同的场景下使用。具体来说,如果你知道目标实体的确切位置(即在哪个服务器上),那么你可以使用InstanceId来发送消息。如果你不知道目标实体的位置,那么你可以使用UnitId来发送消息,框架会自动查询目标实体的位置并发送消息。

// 如何获取服务器InstanceId StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.GetBySceneName(session.DomainZone(), "LoginCenter"); long loginCenterInstanceId = startSceneConfig.InstanceId; // 服务器ID拿出来 L2A_LoginAccountResponse l2ALoginAccountResponse = (L2A_LoginAccountResponse) await ActorMessageSenderComponent.Instance. Call(loginCenterInstanceId, new A2L_LoginAccountRequest() { 
                    AccountId = account.Id }); // 发送请求给这个ID 

❗❗❗18:为什么动画组件无效无法控制动画?

将AnyState连接到你需要的动画上,点击箭头这条线,创建Trigger
前提是MotionType里面有这个变量,有定义这个动画名
在这里插入图片描述
在这里插入图片描述


2、基础知识补充

(1).Mono打包

Mono本身是一个虚拟机,因为C#本身是运行在DotNet平台上面的,而DotNet平台本身就是一个基于虚拟机的平台,Mono虚拟机是对DotNet虚拟机的跨平台移植。

选择Mono方式打包出来的程序只能支持32位的程序。如果使用Mono方式打包,那么就只能打一个32位的系统包,这也就意味着你的程序虽然跑在64位的系统上,但它只能作为一个兼容的32位程序来运行,最多只能支持使用四个G的内存1。对于一个大型游戏而言,只使用4个G的内存是完全不够用的。所以要注意:使用Mono方式打包的程序不支持64位系统。

(2).32位和64位的区别

处理内存大小不同:32位操作系统最多只能识别4GB内存,而64位操作系统最多可识别数十TB的内存。

支持的处理器不同:64位的操作系统支持基于64位的处理器,而32位的系统却不能完全支持64位的处理器。

运行的软件不同:32位的系统只能运行32位的软件程序,但64位的可以向下兼容,运行32位的软件程序。

处理数据的能力:32和64表示CPU可以处理最大位数,一次性的运算量不一样,理论上64位的会比32位快1倍,内存寻址也不一样。

系统大小不同:32位操作系统相对于64位操作系统会稍小一些。

兼容性:32位的操作系统,支持基于32位的软件,不能运行64位的软件;而64位的系统一般这两种类型的都支持,基本上与各种软件都兼容。

总的来说,64位的系统和CPU在处理数据的能力和内存支持上都优于32位的,但是也需要更多的资源

(3).MONO和IL2CPP打包的区别?

简单说 Mono和IL2cpp这两种打包模式,前者只能打包出32位的程序,后者可以打包出更大的程序。由于C#本身就是在dotnet的虚拟机上运行,Mono打包等于是把这些代码放到Mono这个虚拟机上面,然后封装起来,方便我们移植其他平台使用,IL2CPP打包兼容性更强,它会把程序中的IL代码替换成C++方便其他平台编译 ,它的性能比mono高,速度快,而且每个平台都有良好优化的c++编译器,所以用IL2CPP打包出来的程序方方面面都比Mono打包出来的程序高效好用

(4).帧同步、状态同步、状态帧同步

帧同步:客户端按照帧速率,也就是每一个tick上传玩家的所有操作指令,然后发给服务器,服务器转发给其他客户端(服务端主要是校验每个玩家的数据是否正常,反外挂等等),其他客户端在本地在执行这些指令,使得所有玩家看到的画面一致。

状态同步:客户端一旦发生某种状态变化,比如玩家进入冒险关卡,玩家血量不足,蓝量变少。客户端就会收集数据发送给服务端,服务端计算完之后将结果返回客户端。

状态帧同步:客户端在每个tick都会向服务器发送消息,无论状态是否发生变化。这样,服务器可以实时获取到每个客户端的状态,从而保证了游戏的同步。但是,这种方式可能会导致网络负载较大,因为即使状态没有发生变化,也需要发送数据包。所以在设计时,需要权衡网络负载和游戏同步的需求。

(5).客户端是如何预测的?

比如我玩CS,我按下W的那一刻,人物前进是在本地客户端直接执行的。执行完之后传输到服务端,服务器返回之后客户端再校验,看看客户端这个预测是否准确,如果一样那就没问题,如果不一样那就“回滚”,也就是我们游戏中延迟高的时候产生的“瞬移回原位”

(6).什么是UDP(用户数据报协议)?

UDP(用户数据报协议)是一种无连接的、不可靠的、面向报文的传输层协议。它的主要特点是简单快速,但由于其不可靠性,可能会导致数据包丢失。然而,有些应用场景并不需要TCP那样严格的可靠性,例如实时游戏或流媒体传输。


功能开发思路


3、ET7.2与8.0 实践

1.Unity打开之后却没有出现ET菜单(BuildTool)?

在这里插入图片描述
2.ET底层框架原理

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/111234.html

(0)
上一篇 2026-01-26 10:00
下一篇 2026-01-26 10:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信