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

游戏中帧同步的实现

程序员文章站 2022-03-16 21:36:59
...
                     

简介

帧同步是一种前后端数据同步的方式,一般应用于对实时性要求很高的网络游戏,常见于dota类和RTS类游戏,如端游中的dota,dota2,梦三国等;手游中的王者荣耀,*之战等。

过程

帧同步的过程可以简述为:

  1. 各客户端实时上传操作指令集;
  2. 服务端保存这些操作指令集,并在下一帧将其广播给所有客户端;
  3. 客户端收到指令集后分别按帧序执行指令集中的操作。

示例

目前我们正在做的是一款格斗手游,下面是我们项目中使用的同步算法主要伪代码:

1 各客户端实时上传操作指令集

def op_fun():    net.send_lock_step_data(cmd)
  • 1
  • 2

2 服务端保存指令集,并在下一帧广播指令集

def update_lockstep_data(self, cmd):    """保存操作指令"""    role_ctrl_data = self.lockstep_data.setdefault('c', {})    ctrl_data = role_ctrl_data.setdefault(self.uid, {})    ctrl_data.update(cmd)def on_lockstep(self, tid):    """定时器响应函数,广播操作指令"""    self.lockstep_frame_index += 1  # 帧序增加,开始帧同步时初始为0    self.broad(self.lockstep_data)    self.lockstep_data = {'i': self.lockstep_frame_index, 't': time.time()}  # 更新数据,为下一帧做准备
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3 客户端处理收到的帧数据

def recv_lock_step_data(self, data):    self.lockstep_datas.append(data)def update(self):    frame = self.lockstep_datas[0]['i']    if frame == self.lockstep_frame:        ls_data = self.lockstep_datas.pop(0)        ctrl_datas = ls_data.get('c')        if ctrl_datas:            uid_list = ctrl_datas.keys()            uid_list.sort()            for uid in uid_list:                self.process_lockstep_ctrl_data(uid, ctrl_datas[uid])  # 将操作指令给指定玩家        self.lockstep_frame += 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

另外,帧同步还有一点比较重要,要保证各个客户端随机种子相同,各个实体排序也必须一样。

小结

我们用的是乐观帧同步,服务端不会每帧等待每个客户端数据,防止其他客服端被开始;
使用帧同步的好处是各个客户端可以保证数据的高度一致性,带来的问题是调试相当麻烦,需要添加很多的log,来判断具体是那一帧开始不同步,才能进一步的找出为什么不同步。