欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

帧同步之从零开始

程序员文章站 2022-03-16 21:41:29
...

一直想写一个多人在线游戏,也一直在研究相关的技术。从目前了解到的信息来看,网络游戏的同步方案大部分情况下是:帧同步或状态同步。下面就介绍一下,我对帧同步的一些了解。

基本概念


什么是帧同步?简化的流程如下图。

红色箭头:客户端向服务端发送玩家的操作指令。(A-A: 玩家A按下A键)

蓝色箭头:服务器每隔一段时间T,向所有客户端发送当前收集到的客户端指令。

帧同步原理就是这么简单:同步玩家的所有操作而非状态(HP、攻击力、位置)。当客户端收到服务端的消息时,进行游戏状态更新,即图中蓝色箭头位置。所以理想状态下,所有客户端的更新都是同步的。因为绘制时间点是统一的,指令也是一致的,那么就是完美的同步了。

网络延迟


那么问题来了,如果出现网络延迟怎么办?

实际渲染时,玩家A将比玩家B更早收到消息,意味着玩家A的设备里角色已经开始动了而玩家B的设备里游戏角色还是没有反应,即不同步。如果不作处理,随着网络波动,将会导致游戏进行到一定程度后,玩家A和玩家B是两个不同的游戏画面。

优化方案


其实,影响不同步的因素有很多,网络延时可能是最直观的。下面我列举一下,别人和我自己实践中用到的优化方案。

  1. 避免浮点数运算(不同设备下会出现精度不一致的情况,效应叠加就会“差之毫厘谬以千里”)
  2. 减小每次发包的数据量(网络游戏是频繁通信的:10-20次/s)
  3. 逻辑帧与渲染帧(不同设备性能不一,不能将所有游戏的状态更新都放在渲染循环里)
  4. 不同步的插值优化(网络问题的导致的不同步,需要优化处理,手段不一)

实践


为了研究帧同步,写了一个TANK.IO。项目后续会更新,现在这个实现并不完美。

主要的优化操作:

  • 避免浮点数运算

    对小数进行有效位数限定。

    export function toFixed(i, precision=3) {
      return +i.toFixed(precision)
    }
    复制代码
  • 利用msgpack压缩数据,socket.io有对应插件

    const
      parser = require('socket.io-msgpack-parser'),
      io = require('socket.io')(3000, {parser})
    复制代码

PS: 不同步的优化感觉不是很好,就不发出来了。可以本地运行一下代码,在局域网下运行,游戏整体还过得去。

参考资料


从《王者荣耀》来聊聊游戏的帧同步