【Unity】网络同步方案 帧同步与状态同步

前言

本文会详细说明说明帧同步与状态同步、细说实现原理、分析他们的优缺点。提及注意点、保证同步的方法、验证同步、防作弊防攻击等。

帧同步

简单来说,就是相同的状态+相同的指令+ 按帧顺序执行=相同的结果。
状态:所有客户端确保逻辑一致,接收一样的随机种子(randomseed),一样的房间信息;
指令:服务器只负责收集接收每个客户端操作指令(cmd),转发指令,服务器以恒定帧率(30帧1秒)派发指令,没有指令或指令没有变化也需要派发;
执行:真正游戏逻辑由各个客户端单独计算 ,客户端需要收到服务器派发的指令才能推进逻辑,没有收到指令时不能推进逻辑(LockStep)

顺序执行

帧同步会必定按到从第一帧开始一帧一帧的执行,才能保证运行结果一样,跳帧会导致逻辑不一样,如果玩家网络不好,则会在当前帧等待至下一帧的接受,如果丢包超时,则会再次发出需要帧的请求。

追帧

什么是追帧:当前玩家播放到帧比服务器的帧落后时,服务器下发多个帧,玩家便要开始快进到服务器当前帧
为什么要追帧:如果网络波动,服务器会有最晚的接受帧时间,
做法:超过则下次发送多个帧,然后快进播放(多次DoAction),快进期间,不播放特效音效等不影响运行结果的逻辑

重连

做法:接受从0开始所有帧重新快速播放到当前帧,如果帧列表count大于规定速度则按照最大速度播放,否则按照剩余的count播放相应次数的帧。

优点

第一,它的开发效率比较高。如果你开发思路的整体框架是验证可行的,如果你把它的缺点解决了,那么你的开发思路完全就跟写单机一样,你只需要遵从这样的思路,尽量保证性能,程序该怎么写就怎么写,服务端逻辑简单,只需要负责转发指令,压力也小。

比如我们以前要在状态同步下面做一个复杂的技能,有很多段位的技能,可能要开发好几天,才能有一个稍微过得去的结果,而在帧同步下面,英雄做多段位技能很可能半天就搞定了。

第二,它能实现更强的打击感,打击感强除了我们说的各种反馈、特效、音效外,还有它的准确性。利用帧同步,游戏里面看到这些挥舞的动作,就能做到在比较准确的时刻产生反馈,以及动作本身的密度也可以做到很高的频率,这在状态同步下是比较难做的。

第三,它的流量消耗是稳定的。大家应该看过《星级争霸》的录像,它只有几百K的大小,这里面只有驱动游戏的输入序列。帧同步只会随着玩家数量的增多,流量才会增长,如果玩家数量固定的话,不管你的游戏有多复杂,你的角色有多少,流量消耗基本上都是稳定的。

四,这点延伸开来还有一个好处,就是可以更方便地实现观战,录像的存储、回放,以及基于录像文件的后续处理。

缺点

第一,最致命的缺点是网络要求比较高,帧同步是锁帧的,如果有网络的抖动,一段时间调用次数不稳定,网络命令的延迟就会挤压,引起卡顿。

第二,它的反外挂能力很弱,帧同步的逻辑都在客户端里面,你可以比较容易的修改它。但为什么《王者荣耀》敢用帧同步,一方面是因为当时立项的时候开发周期很短,半年时间要做上线,要有几十个英雄,存在时间的压力,另一方面,MOBA类游戏不像数值成长类的游戏,它的玩法是基于单局的,单局的作弊修改,顶多影响这一局的胜负,不会存档,不会出现刷多少钱刷多少好的装备的问题,而且作弊之后我们也很容易监测到,并给予应有的惩罚,所以我们认为这不是致命的缺点。

第三,它的断线重回时间很长,相信台下也有很多王者玩家,也曾碰到过闪退以后重回加载非常长的情况,甚至加载完以后游戏也快结束了,这是帧同步比较致命的问题。

第四,它的逻辑性能优化有很大的压力。大家应该没有见到哪一款大型游戏是用帧同步来做的,因为这些游戏的每一个逻辑对象都是需要在客户端进行运算的。如果你做一个主城,主城里面有上千人,上千人虽然玩家看不到它,但游戏仍然需要对他们进行有效的逻辑运算,所以帧同步无法做非常多的对象都需要更新的游戏场景。

五,debug困难,出现不同的情况,难以查找问题所在,一般通过debug输出关键改变信息来定位问题,但问题可能在1-20个函数之内,但只在第20个函数打了debug信息,然后需要一层层去查找出现问题的所在,考虑把出问题的局录下来,然后不断重播和调试,方便找到问题

协议:

结构
1.roomInfo ①randseed ②role ③...
2.event ①int frame ②event type ③byte[] param
3.cmd ①int frame ②int[] cmd

event示例
1.房间信息(角色数量和角色信息)
2.角色死亡/重生
3.生成怪物/Boss
4.玩家信息改变(换装,位移)
5.游戏时间结束

保证同步

  • 同样的逻辑帧数(10-30),渲染帧可以更高(30以上)
  • 随机数、Time的接管
  • 接管物理的update,Physics.SyncTransforms  Physic.Simulate(time)
  • 顺序一致    角色的创建顺序、各种玩法的创建顺序、OnTriggerXX的顺序
  • 帧同步驱动各系统update代码,不使用Mono的update和Invoke等、
  • 写功能要注意 功能逻辑符合多人玩法,区分音效特效震动的播放,对是否自己的判断
  • 帧同步会动画突然变化 不过帧同步的都无法避免吧 做一些过度和假动画无逻辑(例如转身和走)前端照常播放动画 不会但不会自己切换动画处于一个循环,只有接收到服务器的cmd进行下一个操作而转变动画才会跳到下一个动画
  • 浮点数处理   方案一、定点数   方案二、公式尽量简化(随机float 转int),并且截取一定的精度范围(目前是小数点后3位)
  • 不使用不确定性的插件、例如有时间的、随机数的

如何验证同步

1.把一些关键的运行(例如cmd,位置,动画,攻击,发射子弹等)在一帧的最后,整理为一个string然后转hashcode,上传(①int 帧 ②int hashcode)至服务器,服务器对比hashcode,如果hashcode不一样,则发生了不一样的逻辑,然后服务器去请求这一帧的详细日志(string DebugDetail),获取回来有何不一样。

2.做验证服校验哪里不一样,既是把让服务器把cmd、相关玩家数据和随机种子存下来,然后再播放一次本局然后一次性上传日志,比较差异。

不同步debug记录

  1. 物理结果
    问题:当刚体同时进入到了2个碰撞体,有可能会在不同客户端计算出不同的结果,无法控制。
    解决:不使用物理力kinimatic
  2. 浮点数不一样
    裁剪小数点,尽量用整数计算,这个不好,用定点数好
  3. 不能使用unity的mono、time、 random、 Invoke,因为时机和结果每个客户端不确定。
  4. 据说rootmotion也会可能不一样?
  5. 据说导航结果也会不一样,因为多个角色运动互相挤兑会导致不一样的结果?还是导航结果会不一样?

状态同步

将其他玩家的状态行为同步的方式,一般情况下AI逻辑,技能逻辑,战斗计算都由服务器运算,将运算的结果同步给客户端,客户端只需要接受服务器传过来的状态变化,然后更新自己本地的动作状态、Buff状态,位置等就可以了,但是为了给玩家好的体验,减少同步的数据量,客户端也会做很多的本地运算,减少服务器同步的频率以及数据量。

优点

第一,它的安全性非常高,外挂基本上没有什么能力从中收益。

第二,状态同步对于网络的带宽和抖动包有更强的适应能力,即便出现了200、300的输入延迟再恢复正常,玩家其实也感受不到不太舒服的地方。

第三,在开发游戏过程中,它的断线重连比较快,如果我的游戏崩溃了,客户端重启之后只需要服务器把所有重要对象的状态再同步一次过来,重新再创建出来就可以了。

第四,逻辑性能优化有优势,它的客户端性能优化优势也比较明显,比如优化时可以做裁剪,玩家看不到的角色可以不用创建,不用对它进行运算,节省消耗。

缺点

第一,它的开发效率低,相对帧同步而言要差一些,很多时候你需要保证服务器与客户端的每一个角色对象的状态之间保持一致,但事实上你很难做到一致。

比如客户端和服务器端更新的频率,对优化的一些裁剪,网络的抖动等等,你要让每一个状态在客户端同步是比较难的,而你要想调试这些东西,来优化它带来的漏洞、不一致的现象,花费的周期也会比较长,想要达到优化好的水平也比较难。

第二,打击感差,它比较难做出动作类游戏打击感和精确性。比如说你要做一个射击类角色,他的子弹每秒钟要产生几十颗,基于状态同步来做是比较难的,因为系统在很短时间内,会产生很多数据,要通过创建、销毁、位置运算来同步。

第三,它的流量会随着游戏的复杂度,而逐渐增长,比如角色的多少。希望在3G、4G的网络条件下也能够玩PvP,所以我们希望它对付费流量的消耗能控制在比较合理的水平,不希望打一局游戏就消耗几十兆的数据流量。

防攻击

来自熊猫:
1.特别是搞棋牌的项目,还有小公司没法通过法律手段来防止别人攻击。特别有用处。因为高防实在太贵,用不起。
2. 需要有很多ip,客户端能够随时切换连接,这样别人攻击一个可以切换其他的进程去连接。
3. 需要保证状态不丢失,消息不丢失不重发,显然tcp做不到。
4. 使用udp。因为udp是无连接的
5. 需要保证消息可靠,所以kcp是非常合理的选择
6. 可以设计一个路由进程来转发,udp消息通过路由再转发给realm gate等等
7. 路由进程可以起非常多个,客户端在连接realm或者gate之前先请求路由进程,告诉路由进程自己需要真正连接的地址,路由进程记录下来。然后客户端用kcp连接,路由进程把发过来的udp消息转发给真正的地址,比如gate。所以服务端对外的是路由进程,gate realm变成了内网地址。
8. 客户端连接会每隔2秒ping一次,ping超过10s没有回消息则重新请求一个路由来连接。这样别人攻击一台路由我们就可以不停的关闭被攻击的路由进程,或者开启新的路由都可以。
9.因为udp无连接状态,kcp会保证不丢消息会重发,所以即使换了路由进程,仍然能够保证消息一致性

参考链接:

MMORPG服务器架构 MMORPG服务器架构_大头狗的专栏-CSDN博客_mmorpg架构

tx王者 复盘王者荣耀手游开发全过程,Unity引擎使用帧同步放弃状态同步_any-CSDN博客_王者荣耀手游开发

熊猫:状态同步做魔兽争霸3也是可以的,每个小兵不是时时刻刻都在变化移动路线,团战的时候大部分小兵都是站着不动的。移动消息量其实不大。移动消息改成发送移动路径,只要小兵移动路径不变,就不需要发送新的移动消息,状态同步完全可以做rtx。个人

有工具把unity的mesh导出成recast格式的mesh文件,然后自带的demo生成寻路文件。研究下吧,会c++就不难,一个星期可以搞定

观点

  • 11
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值