6.0 疑问可以直接看 13.ET基础问题总结
ET
- 1、ET 框架基础
- (1).前后端通讯流程Demo
- (2).登录系统Demo总结
- (3).ESC编程原则
- (4).组件生命周期
- (5).Scene层级树
- (6).关于Scene的一些疑问
- (7).Excel配置工具使用
- (8).ET框架事件系统
- (9).ETTask异步编程
- (10).ProtoBuf通讯消息(非Google开源C++版本)
- (11).网络通讯消息编写
- (12).Actor模型
- (13).ET服务器与客户端之间登录流程
- (14).服务器的编写流程
- (15).UI界面入门
- (16).公共UI的创建与使用
- (17).登录模块Realm、Gate流程
- (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).角色跟随摄像机功能
- (20).AOI视野模块
- (21).AOI视野模块源码解析
- (22).关于不同消息处理抽象类的使用
- ⭕ET基础问题总结
- 1:`我的组件没有ID这个属性,从何而来? `
- 2:`ATimer类与计时器类与run方法的关系`
- 3:`组件与客户端关联吗?每个玩家的客户端上都有自己的组件吗?`
- 4:`ET框架服务器是单线程,多个玩家同时请求同一个组件的时候如何执行?`
- 5:`只要不使用async就一定不会异步执行吗?`
- 6:`awake生命周期函数的执行时间点`
- 8:`domainScene、zoneScene究竟是什么?`
- 9:`Excel配置中127.0.0.1:10005地址和端口究竟是啥?`
- 10:`为什么游戏物体添加了多个组件,只返回其中一个,返回内容同样可以包含其他组件`
- 11:`为什么我Unity的 文字是 TMP不能显示,或者显示为中文乱码`
- 12:`Call、CallActor、CallLocationActor的区别?`
- 13:`publishasync出现:event error: xxxxxxxxxxxxxxxxxxx `
- 14:`Unity中的代码该如何Debug? `
- ❗❗❗15:`IDeserialize 和 ISerializeToEntity 之间有什么作用?`
- ❗❗❗16:`如何将消息发给自定义服务器?`
- ❗❗❗17:`如何将消息发给服务器?`
- ❗❗❗18:`为什么动画组件无效无法控制动画?`
- 2、基础知识补充
- 功能开发思路
- 3、ET7.2与8.0 实践
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编程原则
1、实体既组件,组件既实体。
2、如要编写一个新的实体或者组件,绝不继承除Entity之外的任何父类!
3、绝不使用任何的虚函数,使用逻辑分发替代。
4、Model
和ModelView
只存放实体和组件的数据字段声明,如非必要绝不放任何逻辑函数
5、Hotfix
和HotfixView
中只保留纯逻辑函数,也就是使用静态类和扩展方法编写的System,且绝不允许存在任何数据字段。
6、Model
和Hotfix
中绝不允许出现跟Unity3d引擎相关的游戏对象类和调用相关API函数。
7、如实体或组件有数据字段声明必须编写相关生命周期函数,以防实体对象池回收再利用导致逻辑错误。
要严格遵循要求,不同文件放置不同的内容
更新之后组件接口出 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# 中,可以使用async
和await
关键字来实现异步编程。例如,可以定义一个 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).网络通讯消息编写
下方实例中我们重新编写ET框架中的Login
针对call()方法定制的相应方法,注意类名上方的特性声明
编写完四条消息的函数之后编译完就可以在控制台和日志中查看输出结果
(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界面入门
Dlg文件:表示窗口。
将下方UI拖入NormalRoot,修改名字后再重新拖入下方,则复制了一个场景。
然后右点击SpawnEUICode
就可以生成文件,ET-EUI-main\Unity\Codes\ModelView\Demo\UI\DlgTest
如遇报错,则进入D:\GameWork\ET-EUI-main\Unity\Codes\HotfixView\Demo\UI\DlgTest\Event\DlgTestEventHandler.cs
中
添加新的窗口ID,并指定
在Unity中添加AssetBundle
登录成功之后,隐藏登录页,显示我们创建的测试窗口
(16).公共UI的创建与使用
EUI使用教程
看完这四集还要自己手动尝试一下才能明白自己编写的使用。
(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主要是针对服务器之间的通信。
这些类都是抽象类,你不能直接使用它们,而是需要创建它们的子类来处理特定的消息。你可以根据需要创建自己的消息处理类,但是你需要确保你的类继承自正确的基类,并且正确地实现了所需的方法
此外,Actor模型确实主要用于处理服务器之间的通信。通过使用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生命周期函数的执行时间点
总之awake方法是在组件还没挂在好的时候开始执行,一旦awake执行完成等于组件已经挂在好了。这个时候Load方法开始执行
7:客户端与组件、服务器与组件之间有什么区别?
在客户端中,所有unity的实体都塞给客户端做组件。但是在服务器中,由于有太多服务器,所以我们根据需求把对应的实体给对应的服务器做组件,是选择性的添加。
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:为什么游戏物体添加了多个组件,只返回其中一个,返回内容同样可以包含其他组件
在Unity中,游戏对象(GameObject)可以被看作是一个容器,它可以包含多个组件(Component)。这些组件定义了游戏对象的行为和显示方式。例如,Button组件使游戏对象可以作为一个按钮使用,而Text组件则允许游戏对象显示文本。
当我们创建一个新的按钮时,我们实际上是在创建一个新的游戏对象,并向其添加了Button和Text两个组件。这意味着,这个游戏对象既有按钮的功能,也能显示文本。
所以,即使我们只返回了Button组件,由于它是附加到游戏对象上的,我们仍然可以通过这个游戏对象来访问其它的组件,包括Text组件。这就是为什么我们可以设置按钮的文本,即使我们只返回了Button组件。
11:为什么我Unity的 文字是 TMP不能显示,或者显示为中文乱码
12:Call、CallActor、CallLocationActor的区别?
Call:这个方法通常用于客户端向服务器发送同步请求。这种请求会阻塞当前线程,直到服务器响应。
CallActor:这个方法通常用于客户端向服务器发送异步请求。这种请求不会阻塞当前线程,服务器会在处理完请求后返回结果。
CallLocationActor:这个方法通常用于服务器之间的通信。它允许一个服务器向另一个特定位置的服务器发送请求。
13:publishasync出现:event error: xxxxxxxxxxxxxxxxxxx
出现类似错误因为你的消息拦截类,使用的是AEvent<> 而不是 AEventAsync<>
14:Unity中的代码该如何Debug?
启动server.app debug第二个内容
❗❗❗15:IDeserialize 和 ISerializeToEntity 之间有什么作用?
两者都能进行序列化与反序列化,ISerializeToEntity 的组件一般是 IDeserialize 的子组件
ISerializeToEntity 在使用 AddComponent 或 Addchild
的时候,源码中 [BsonElement("C")] 自动序列化到数据库中 名为"C"
IDeserialize 在使用 AddComponent 或 Addchild
的时候,源码中 [BsonElement("Children")] 自动序列化到数据库中 名为"Children"
注: 实现前两个接口进行序列化,不能常规的使用DBManager来简单的搜索出数据内容。
通常在组件System中实现接口的方法,进行反序列化
例如:
// 反序列化
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.cs
中DispatchAsync
方法,在这个方法中,将接口进行判断,不同的接口做不同的处理,实际上也就是不同的接口表示不同服务器之间的消息通讯
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那样严格的可靠性,例如实时游戏或流媒体传输。
功能开发思路
好友功能需要用户A向用户B发送消息请求,而ET框架中自带的MessageHelper转发消息需要得到角色的GateSessionActorId,所以我决定先创建一个组件用来存储所有登陆过的角色的名字以及GateSessionActorId。
在角色进入游戏的时候调用这个方法,传入角色的名字和GateSessionActorId;
为了保证数据的稳定就不适用序列化接口,直接自己存储到数据库
编写发送好友请求的部分
3、ET7.2与8.0 实践
1.Unity打开之后却没有出现ET菜单(BuildTool)?
##2.ET底层框架原理