帧同步优化难点及解决方案_帧同步中 如果其中一个客户端卡顿怎么办

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

Skywind Inside  再谈网游同步技术

http://www.skywind.me/blog/archives/1343#more-1343

文章里面提到的Time Warp方式,我理解回滚逻辑,和Time Warp是一个概念。

  • 游戏逻辑的回滚

回滚逻辑,就是我们解决问题的方案。可以这样理解,客户端的时间,领先服务器,客户端不需要服务器确认帧返回才执行指令,而是玩家输入,立刻执行(其他玩家的输入,按照其最近一个输入做预测,或者其他更优化的预测方案),然后将指令发送给服务器,服务器收到后给客户端确认,客户端收到确认后,如果服务确认的操作,和之前执行的一样(自己和其他玩家预测的操作),将不做任何改变,如果不一样(预测错误),就会将游戏整体逻辑回滚到最后一次服务器确认的正确帧,然后再追上当前客户端的帧。

此处逻辑较为复杂,我尝试举个例子说明下。

当前客户端(A,B)执行到100帧,服务器执行到97帧。在100帧的时候,A执行了移动,B执行了攻击,A和B都通知服务器:我已经执行到100帧,我的操作是移动(A),攻击(B)。服务器在自己的98帧或99帧收到了A,B的消息,存在对应帧的操作数据中,等服务器执行到100帧的时候(或提前),将这个数据广播给AB。

然后A和B立刻开始执行100帧,A执行移动,预测B不执行操作。而B执行攻击,预测A执行攻击(可能A的99帧也是攻击),A和B各自预测对方的操作。

在A和B执行完100帧后,他们会各自保存100帧的状态快照,以及100帧各自的操作(包括预测的操作),以备万一预测错误,做逻辑回滚。

执行几帧后,A和B来到了103帧,服务器到了100帧,他开始广播数据给AB,在一定延迟后,AB收到了服务器确认的100帧的数据,这时候,AB可能已经执行到104了。A和B各自去核对服务器的数据和自己预测的数据是否相同。例如A核对后,100帧的操作,和自己预测的一样,A不做任何处理,继续往前。而B核对后,发现在100帧,B对A的预测,和服务器确认的A的操作,是不一样的(B预测的是攻击,而实际A的操作是移动),B就回滚到上一个确认一样的帧,即99帧,然后根据确认的100帧操作去执行100帧,然后快速执行101103的帧逻辑,之后继续执行104帧,其中(101104)还是预测的逻辑帧。

因为客户端对当前操作的立刻执行,这个操作手感,是完全和PVE(不联网状态)是一样的,不存在任何Delay。所以,能做到绝佳的操作手感。当预测不一样的时候,做逻辑回滚,快速追回当前操作。

这样,对于网络好的玩家,和网络不好的玩家,都不会互相影响,不会像Lockstep一样,网络好的玩家,会被网络不好的玩家Lock住。也不会被网络延迟Lock住,客户端可以一直往前预测。

对于网络好的玩家(A),可以动态调整(根据动态的Latency),让客户端领先服务器少一些,尽量减少预测量,就会尽量减少回滚,例如网络好的,可能客户端只领先2~3帧。

对于网络不好的玩家(B),动态调整,领先服务器多一些,根据Latency调整,例如领先5帧。

那么,A可能预测错的情况,只有2~3帧,而网络不好的B,可能预测错误的帧有5帧。通过优化的预测技术,和消息通知的优化,可以进一步减少A和B的预测错误率。对于A而言,战斗是顺畅的,手感很好,少数情况的回滚,优化好了,并不会带来卡顿和延迟感。

重点优化的是B,即网络不好的玩家,他的操作体验。因为客户端不等待服务器确认,就执行操作,所以B的操作手感,和A是一致的,区别只在于,B因为延迟,预测了比较多的帧,可能导致预测错,回滚会多一些。比如按照B的预测,应该在100帧击中A,但是因为预测错误A的操作,回滚重新执行后,B可能在100帧不会击中A。这对于B来说,通过插值和一些平滑方式,B的感受是不会有太大区别的,因为B看自己,操作自己都是及时反馈的,他感觉自己是平滑的。

这种方式,保证了网络不好的B的操作手感,和A一致。回滚导致的一些轻微的抖动,都是B看A的抖动,通过优化(插值,平滑等),进一步减少这些后,B的感受是很好的。我们测试在200~300毫秒随机延迟的情况下,B的操作手感良好。

这里,客户端提前服务器的方式,并且在延迟增大的情况下,客户端将加速,和

守望先锋的处理方式是一样的。当然,他们肯定比我做得好很多:

https://mp.weixin.qq.com/s/cOGn8-rHWLIxdDz-R3pXDg

希望我已经大致讲清楚了这个逻辑,大家参看几篇链接的文章,能体会更深。

这里,我要强调的一点是,我们这里的预测执行,是真实逻辑的预测,和很多介绍帧同步文章提到的预测是不同的。有些文章介绍的预测执行,只是View层面的预测,例如前摇动作和位移,但是逻辑是不会提前执行的,还是要等服务器的返回。这两种预测执行(View的预测执行,和真实逻辑的预测执行)是完全不是一个概念的,这里需要仔细地区分。

这里有很多的可以优化的点,我就不一一介绍了,以后可能零散地再谈。

  • 游戏逻辑的快照(snapshot)

我们的逻辑之所以能回滚,都是基于对每一帧状态可以处理快照,存储下每一帧的状态,并可以回滚到任何一帧的状态。

Understanding Fighting Game Networking文章:

http://mauve.mizuumi.net/2012/07/05/understanding-fighting-game-networking/

守望先锋网络文章:

https://mp.weixin.qq.com/s/cOGn8-rHWLIxdDz-R3pXDg

以上两篇文章都一笔带过了快照的说明。他们说的快照,可能略有不同,但是思路,都是能保存下每一帧的状态。如果去处理快照(Understanding那篇文章做的是模拟器游戏,可以方便地以内存快照的方式来做),是一个难点。

这也是我前面文章,提到ECS在这个方式下的应用:

https://zhuanlan.zhihu.com/p/38280972

云风的解释:

云风博客截图,地址https://blog.codingnow.com/2017/06/overwatch_ecs.html

ECS是一个好的处理方式,并且我找到一篇文章,也这样做了(我看过他开源的demo,做得还不够好,应该还是demo阶段,不太像是一个成型的项目)。

https://www.kisence.com/2017/11/12/guan-yu-zheng-tong-bu-de-xie-xin-de/

这篇文章的思路是很清晰的,并且也点到了一些实实在在的痛点,解决思路也基本是正确的,可以参看。

这块我做得比较早了,当时守望先锋的文章还没出,我的战斗也没有基于ECS,所以,在处理快照上,只有自己理顺逻辑来做了。

我的思路是,通过一个回滚接口,需要数据回滚的部分,实现接口,各自处理自己的保存快照和回滚。就像我们序列化一个复杂的配置,每个配置各自序列化自己的部分,最终合并成一个序列化好的文件。

首先,定义接口,和快照数据的Reader和Writer

然后,就是每个模块,自己去处理自己的TakeSnapshot和Rollback,例如:

简单的数值回滚

复制的列表回滚和调用子模块回滚

思路理顺以后,就可以很方便地处理了,注意Write和Read的顺序,注意处理好List,就解决了大部分问题。当然,在实现逻辑的过程中,时刻要注意,一个模块如何回滚(例如获取随机数也需要回滚)。

有一个更简单的方式,就是给属性打Attribute,然后写通用的方法。例如,我早期的实现方案:

给属性打标签

根据标签,通用的读写方法,通过反射来读写,就不需要每个模块自己去实现自己的方法了:

部分代码

这种方法,能很好地解决大部分问题,甚至前面提到的Truesync,也是用的这种方式来做。

但是这种方法有个难以回避的问题,就是GC,因为基于反射,当我们调用field的GetValue和SetValue的时候,GC难以避免。并且,因为全自动,不方便处理一些特殊逻辑,调试优化也不方便,最后改成了现有的方式,虽然看起来笨重一些,但是可控性更强,我后续做的很多优化,都方便很多。

关于快照,也有很多可以优化的点,无论是GC内存上的,还是运行效率上的,都需要优化好,否则,可能带来性能问题。这块优化,有空另辟文章再细谈吧。

当我们有了快照,就可以支持回滚,甚至跳转。例如我们要看战斗录像,如果没有快照,我们要跳到1000帧,就需要从第一帧,根据保存的操作指令,一直快速执行到1000帧,而有了快照,可以直接跳到1000帧,不需要执行中间的过程,如果需要在不同的帧之间切换,只需要跳转即可,这将带来巨大的帮助。

  • 自动测试

由于帧同步需要测试一致性的问题,对我们来说,回滚也是需要大量测试的问题。自动测试是必须要做的一步,这块没有什么特别的点,主要就是保存好操作,快照,log,然后对不同客户端的数据做比对,找到不同的地方,查错改正。

我们现在做到,一步操作,自动循环战斗,将每一盘战斗数据上传内网log服务器。

当有很多盘战斗的数据后,通过工具自动解析比对数据,找到不同步的点。也是还可以优化得更好,只是现在感觉已经够用了。经过大量的内部自动测试,目前战斗的一致性,是很好的。

总结

我们现在的帧同步方案,总结下来,就是预测,快照,回滚。当把这些有机地结合起来,优化好,就有了非常不错的帧同步联网效果,无论网络速度如何,只要不是延迟大到变态,都保证了非常好的操作手感。

快照回滚的方式,也不是所有游戏都适用,例如:

Skywind Inside " 再谈网游同步技术文章中对此模式(Time warp或Rollback)的缺点,也说明了。

http://www.skywind.me/blog/archives/1343#more-1343

如图所述,这种模式不适合太多人的联网玩法,例如MOBA,可能就不太适用。我们最多三人联机,目前优化测试下来,效果也没有太大问题。但是联机人数越多,预测操作的错误可能性越大,导致的回滚也会越多。

一篇文章,难以讲得面面俱到,很多地方可能描述也不一定明确,并且,个人能力有限,团队人员有限(3个客户端)的情况下,必定有很多设计实现不够好的地方,大家见谅。

一些有帮助的文章再列一下:

1、Understanding Fighting Game Networking

http://mauve.mizuumi.net/2012/07/05/understanding-fighting-game-networking/

2、Skywind Inside " 再谈网游同步技术

http://www.skywind.me/blog/archives/1343#more-1343

3、《守望先锋》回放技术:阵亡镜头、全场最佳和亮眼表现

https://mp.weixin.qq.com/s/cOGn8-rHWLIxdDz-R3pXDg

4、《王者荣耀》技术总监复盘回炉历程

http://youxiputao.com/articles/11842

5、帧同步:浮点精度测试

https://zhuanlan.zhihu.com/p/30422277

6、A guide to understanding netcode

https://www.gamereplays.org/overwatch/portals.php?show=page&name=overwatch-a-guide-to-understanding-netcode

7、网游流畅基础:帧同步游戏开发

http://www.10tiao.com/html/255/201609/2650586281/4.html

最后啰嗦一句,如最开始所述,帧同步有很多变种、实现方式和优化方向。有时候,可能不同文章提到帧同步这个术语的时候,里面的意思,可能都有区别,大家需要仔细理清和区分。

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

外链图片转存中…(img-ZYBvLeuu-1715806640355)]
[外链图片转存中…(img-uoBdXTbi-1715806640355)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值