Jenner's Blog

不变秃,也要变强!

0%

状态同步的抖动

之前的mmorpg项目中使用了状态同步,但是在移动同步中发现了抖动现象,于是打算研究一下。

分析原因

频率不一致

服务端按固定频率发包,但是客户端不是固定频率收到,如果直接应用服务端状态,必然导致抖动。

解决方案

客户端对收到的状态信息进行缓存入队,然后用状态插值追赶服务器状态。
状态插值:Current = MathUtils.Interpolate(State_A, State_B, 估算帧号/ (State_B.frame -State_A.frame ))
估算帧应该比真实帧号提前一些,否则处于A帧时,B帧还没收到,需要等待。但是会有延迟代价。这个代价可以通过增大发包频率,和客户端预测表现来弥补。
估算帧是客户端估算服务器发来的帧号,当收包帧号与估算差距太大时,需要调整。比如客户端估算帧比服务端提前,那客户端就不模拟,反之加快模拟的速度。

客户端本地预测

如果要等待服务器返回状态才开始表现,对于延迟比较高的玩家可能无法忍受,这时候需要客户端自己本地预测,来预表现。
流程:

  1. 客户端本地模拟指令,并且将指令队列发送给服务器;
  2. 服务器收到指令序列,逐帧模拟,然后向客户端发送模拟结果;
  3. 客户端与收到的模拟结果比对,为指令序列做确认

抖动

服务器返回的模拟结果状态是旧的,客户端已经预表现了,如果直接应用服务器的结果肯定会造成抖动。

解决方案

当客户端收到描述角色状态的数据包时,我们基本上就得把移动状态及时恢复到最近一次经过服务器验证过状态上去,而且必须重新计算之后所有的输入操作(除了服务器验证过的),直至追上当前时刻。

多客户端移动同步

A客户端通过服务器同步B客户端状态时,如果使用预测的方式,A拿到的B状态必然是比A预测的B状态要延迟的,如果直接应用服务器传来的B状态,肯定要导致抖动。并且A拿到的B操作指令也是旧的,没办法和之前那样解决。
这里我使用了状态插值的办法。之后想到更好的解决办法,再来更新。

Valve分享了Source引擎内置的网络同步模型,在旗下的多人游戏都有应用,包括CS:GO,Left 4 Dead 2,Team Fortress 2等等。有时间去参考一下。

点击下方打赏按钮,获得支付宝二维码