游戏开发:帧同步方案 详解

LockStep(同步锁步算法,狭义上的帧同步)

  • 本文主要想理一理帧同步这个概念,告诉大家能在这上面做些什么。
  • 这个概念最早来自于军队,指的是同步行走。
  • 最早应用于RTS局域网游戏。之后市面上的各种即时游戏已经在其基础上进行各种优化调整了,不再是最原始的lockstep模式了。
  • 核心思想:
相同的状态
相同的输入
相同的输出

传统RTS局域网游戏

LockStep

  • 客户端的操作会被打包成一个个指令。
    • 打包时间在当前帧的指令同步时间区(逻辑帧更新频率为100ms的情况下,这个值一般为50-60ms),则会一起同步出去
    • 超出时间的指令则放在下一帧进行同步(或者丢弃,看需求)
    • 当前帧无任何指令,则会同步Idle出去(相当于啥都没干)
    • 这里的转发中枢实际上指定了某台client作为虚拟server来进行的,先收集,后转发
  • 我们发现一帧内留足了缓冲时间,用来接收下一帧运行需要的数据集,在局域网上,这段时间足够充足了。接收到数据集后,就可以运行下一帧了。
  • 如果缓冲时间内收不到下一帧的数据,则表示有人卡了,此时会卡画面或弹等待框。如War3,卡一个人,其他玩家一起等;主机掉线了,游戏立马重开。
  • 有意思的是,在100ms内,下一帧的状态运算短时间内就能被完成(超过10ms的感觉就可以优化了)。剩余的90+ms实际上在逻辑层看来,唯一的工作就是接收指令打个包,99%的时间其实都在干等着。
  • 接下来是表现层的事。
    • 这里有个概念的区分:
      • 逻辑层的状态可以看成是离散的。
      • 表现层的状态是连续的,由逻辑层的离散状态进行插值平滑得出。
    • 表现层的帧率一般都按快的来了,60FPS,能快就快。
  • 这就是最原始的帧同步模型了,在局域网下,简单但效果好。

帧同步2.0

上述的lockstep算法在局域网内算是畅通无阻了,但运用到网络游戏上,由于过大的网络延迟,发包和收包都得不到流畅性保障,对玩家的体验就比较差了,需要进行改造。
姑且称作为帧同步2.0。
乐观帧同步
1. 乐观帧同步

  • Server端收集指令时,不再等待所有Client当前帧的指令。
  • Server自己拥有一个权威的本地时钟来控制帧率。
  • Server收集当前帧下的指令,超时后自动设置那些没收到指令的Client状态为Idle。下发当前帧的指令给所有Client,接着开始下一帧的监听。那些延后收到的指令则会被标记为下一次帧。
  • 这里的好处是,网络状态良好的玩家不会受到其他玩家网络环境问题带来的干扰,网络越好体验就越流畅。至于网络环境差的玩家,只能从其它方向来尽量优化流畅度。

2.逻辑帧缓冲Buffer

  • 由于网络波动影响,Client收到CMD的时间并不稳定,这就导致整个游戏的运行忽快忽慢。因为是一收到下一帧的数据就立马执行的。
  • 这里可以缓存N帧的数据来延迟执行,可以一定程度上抵消网络波动带来的影响,使得表现更为平滑。
  • 但相对应的,N值越大,固有延迟越大,这个得按实际情况来取值,也可以做到根据网络环境自动调节N值。
  • 王者荣耀的N值据说为0,这说明了表现层上的预表现和混合处理做得相当好了,才能无视网络波动使得表现依然平滑稳定。

3. 表现层帧率自适应

  • 当网络环境变差的情况下,我们的帧缓冲Buffer快用完了,或者已经用完了,这时候我们可以通知表现层降低更新频率,增加渲染一逻辑帧所需要的时间,来等待延后到来的下一逻辑帧数据。
  • 当然,下一逻辑帧的数据迟迟未到,最终还是会导致卡屏。这时候还能靠预表现来拯救一波。

帧同步3.0

预表现以及预测

  • 关于增强玩家操作的手感,也就是操作后立马能够在屏幕上得到反馈。
  • 手感其实跟逻辑层没有太大关系,当然逻辑层的帧率越高、响应越即时,手感自然得到提升,例如局域网游戏。但这相当于硬性条件了,而非我们可以采取多种手段的优化项。
  • 正常的情况下,Client做一个操作,并不会马上得到响应,首先需要将指令发送给Server,等待下一帧Server统一返回的权威数据后,这个操作才可以被执行。也就是,响应时间至少在一个逻辑帧频率+来回网络延迟,100ms+以上。所以,即使在网络环境良好的情况下,也不能做到操作即时表现。
  • 这里可以采取的简单措施是:
    • 根据现有的逻辑层数据来判断这个操作是否能必定执行?这里的判断条件最好用规则来约束,比如在第100帧按下了释放一个瞬发眩晕术,我只要规定好5帧以内敌方玩家在我的视野中,我必定可以命中,那Client就可以不用等待Server的回复先行释放眩晕术了,因为表现必定是正常的。
    • 之后要处理的就是收到一帧的正确数据后,逻辑层和表现层的预表现部分进行混合,得到不穿帮的平滑效果就ok。
    • 比如控制角色行走,预表现之后就必须采取一定的机制使得表现层的位置和逻辑层的位置在一段时间后,或者某个特殊情况下是一致的。
  • 预测。怎么处理非我方控制单位的预表现?
    • 这里简单点可以根据一些惯性特征来进行预测,比如按部就班行动的一些AI单位,直线冲过来的其它玩家等等。
    • 一些很频繁受到其它玩家输入影响的实体,就不好进行预测了,预测失败的话还得面临表现回滚的问题(插值纠正或者直接暴力纠正)了。当然这里有一些高逼格的算法专门用于预测这些不稳定的状态,如卡尔曼滤波,RTS平滑滤波。。。

如何动手

我的想法是,想做出点效果,可以支持普通的即时玩法实现,优先做好2.0版本即可,实际上并没有上述所描述的那么简单,这里只提了个概况而已。而想要效果更出众,各方面平滑度手感都贼好,就得在3.0版本上花大功夫。

转载请保留原文:https://blog.csdn.net/heyiwen555/article/details/84591070

猜你喜欢

转载自blog.csdn.net/heyiwen555/article/details/84591070