游戏客户端开发系列 -- 网络同步中的帧同步和状态同步

前言

游戏场景中,玩家 A 释放技能给玩家 C 造成伤害,玩家 B 也要同步看到这样的过程,保证多人联机游戏中游戏的一致性

帧同步

帧同步,也称 LoackStep(锁步同步),是齐步行军的意思

帧同步特点

  • 帧同步的战斗逻辑在客户端
  • 在帧同步下,通信比较简单,服务端只转发操作不做任何逻辑处理

帧同步流程

  • 客户端按照一定的帧速率(理解为逻辑帧,而不是客户端的渲染帧 )去上传当前的操作指令 ,服务端将操作指令广播给所有客户端
  • 当客户端收到指令后执行本地代码,如果输入的指令一致,计算的过程一致,那么计算的结果肯定是一致的,这样就能保证所有客户端的同步,这就是帧同步。

帧同步优缺点

优点

  1. 数据传输量较小
  2. 逻辑计算在客户端 执行,服务器压力小

缺点

  1. 由于帧同步战斗逻辑都在客户端,服务器没有验证,带来的问题就是外挂的产生(加速、透视、自动瞄准、数据修改等)
  2. 由于逻辑在客户端处理,必须保证客户端状态的高度还原,网络条件较差的客户端会影响其他玩家的游戏体验 。(优化方案:乐观帧锁定、渲染与逻辑帧分离、客户端预执行、指令流水线化、操作回滚等)
  3. 不同机器浮点数精度 问题、容器排序不确定性、RPC时序、随机数值计算不统一(优化方案:可以用定点数来将误差控制在一个可接受范围)

帧同步优化方案------乐观帧

为了解决【网络条件较差的客户端会影响其他玩家的游戏体验】的问题,我们采用乐观帧锁定的优化方案:

由【每个玩家有操作客户端才发送消息给服务器 ,然后服务器再通报 】改为【服务端接受并累积客户端操作指令 ,然后服务端定时通报所有客户端】:

  1. 单个用户当前键盘上下左右攻击跳跃是否按下用一个 32 位整数 描述,服务端描述一局游戏中最多 8 玩家的键盘操作为:int player_keyboards[8];
  2. 服务端每秒钟 20-50 次向所有客户端发送更新消息(包含所有客户端的操作和递增的帧号):
  3. update=(FrameID,player_keyboards)
  4. 客户端就像播放游戏录像一样 不停的播放这些包含每帧所有玩家操作的 update 消息。
  5. 客户端如果没有 update 数据了,就必须等待,直到有新的数据到来。
  6. 客户端如果一下子收到很多连续的 update,则快进播放。
  7. 客户端只要按键按下或者放开 ,就会发送消息给服务端 (而不是到每帧开始才采集键盘),消息只包含一个整数。服务端收到以后,改写 player_keyboards

状态同步

状态同步可以理解为在服务器对数据进行状态管理,然后通过客户端将数据以游戏的形式展示出来

状态同步特点

  • 状态同步的战斗逻辑在服务端
  • 在状态同步下,客户端更像是一个服务端数据的表现层

状态同步流程

  • 客户端上传操作到服务器
  • 服务器收到后计算游戏行为的结果 ,然后以广播的方式下发游戏中各种状态
  • 客户端收到状态后再根据状态显示内容

如今的状态同步:增量同步、RPC(远程过程调用)两种同步手段

目前状态同步多用于 CS 架构(Client/Server),客户端通过 RPC 向服务器发送指令信息,服务器通过属性同步(增量状态同步)向客户端发送各个对象的状态信息。我们可以才有预测回滚、延迟补偿、插值等优化方式

状态同步优缺点

优点

  1. 由于每一帧的状态都会进行保存,所以可以更方便的切换到指定的帧状态,进行回滚操作
  2. 符合传统互联网的B/C通信逻辑
  3. 由于逻辑计算都在服务端进行,所以安全性非常高。除非是设计不够完善或是进攻服务器,否则难以被外挂支配

缺点

  1. 开发复杂度较大。由于需要匹配对象的所有参数,所以数据规范化十分重要
  2. 由于每个对象都有大量的自有参数,所以传输数据量也非常庞大,相应的也会影响网络流畅度
  3. 涉及到动作动画等操作,需要修改为量化操作。开发比较复杂
  4. 游戏的核心逻辑计算都在服务器运行,所以服务端压力比较大

帧同步和状态同步的区别

  • 帧同步 的战斗逻辑在客户端状态同步 的战斗逻辑在服务端
  • 状态同步比帧同步流量消耗大,例如一个复杂游戏的英雄属性可能有100多条,每次改变都要同步一次属性,这个消耗是巨大的,而帧同步不需要同步属性;
  • 帧同步的回放&观战比状态同步好做得多,因为只需要保存每局所有人的操作就好了,而状态同步的回放&观战,需要有一个回放&观战服务器,当一局战斗打响,战斗服务器在给客户端发送消息的同时,还需要把这些消息发给放&观战服务器,回放&观战服务器做储存,如果有其他客户端请求回放或者观战,则回放&观战服务器把储存起来的消息按时间发给客户端。
  • 状态同步的安全性比帧同步高很多,因为状态同步的所有逻辑和数值都是在服务端的,如果想作弊,就必须攻击服务器,而攻击服务器的难度比更改自己客户端数据的难度高得多,而且更容易被追踪,被追踪到了还会有极高的法律风险。而帧同步因为所有数据全部在客户端,所以解析客户端的数据之后,就可以轻松达到自己想要的效果。
属性 帧同步(LockStep) 状态同步
确定性 严格确定 允许小误差,定时纠正误差数据
表现与响应速度 传统严格帧锁定要等其他客户端消息全部到达,响应比较慢;乐观帧锁定可以做到本地立刻响应,但是需要回滚的时候,体验就没那么好了 一般会做预测,可以做到立刻响应。不做预测的话,响应时间是一个往返时间(RTT)
带宽与流量 带宽随人数增加而增加,不适合MMO 需要发送各种状态数据,带宽占用比较高。可以通过压缩、裁剪、增量等方式优化。人数较少时候不如帧同步省流量
网络延迟适应性 要求较低的延迟。如果延迟较高,所有玩家体验都不好。即使采用乐观帧锁定优化,高延迟下也容易产生卡顿 适应性较高,方便做各种插值优化。当然高延迟下,也容易产生位置突变
开发难度 初期开发减法,框架容易实现,但是后期解决bug和完善系统很困难。比如浮点数、随机数、执行顺序导致计算结果不一致,问题很难排查 框架比较复杂,客户端服务端一套代码,每个功能都需要客户端服务端联调。问题定位比较容易。也会出现时序问题
玩家数量 适合少量的玩家 ,比如ACTMOBA 可以支持多人玩家游戏(100人及以上),比如绝地求生、和平精英等
跨平台 不适合跨平台,会有浮点数问题,可以用定点数来将误差控制在一个可接受范围,同时可以定时纠正结果 适合跨平台。有权威服务器
反外挂 P2P架构不适合反外挂,如果引入战斗服务器来校验各个客户端结果,可以解决常见外挂,但是透视和全图视野防不了 与服务器加入校验机制,可以起到比较好的反外挂效果。但是一样防不了透视外挂
断线重连 比较复杂 。可以在断线的时候,通过快捷播放服务器同步的帧数据来快速跟上游戏 容易。由于实时记录了各个对象的状态信息,所以重连的时候,直接创建这些对象,并同步信息即可
性能(客户端) 客户端要跑完整逻辑,还要执行渲染逻辑,开销比较大 可以灵活优化,客户端跑较少逻辑
回放(离线) 本身收集了所有玩家的输入信息进行逻辑推进,天然支持回放,且回放文件比较小 可以支持回放,但是逻辑比较复杂,需要不断记录状态信息,同时回放时候需要读取合适的时间。回放文件大
回放(实时) 比较复杂,客户端需要本地对全场状态进行序列化,才能回到目标时间。播完回放后还需要加速追上实时游戏状态 相对容易,可以方便的记录快照信息,并按照录制内容随时播放

帧同步和状态同步混合

这两种方式组合起来就能实现更强大的同步系统

正常同步时使用帧同步 ,当掉线、存档、回滚、或回放的时候,使用状态同步。

参考文章

相关推荐
编程之路,妙趣横生1 小时前
list模拟实现
c++
一只小bit3 小时前
数据结构之栈,队列,树
c语言·开发语言·数据结构·c++
la_vie_est_belle4 小时前
《Cocos Creator游戏实战》非固定摇杆实现原理
游戏·cocos creator·游戏开发·cocos·非固定摇杆
Thomas_YXQ4 小时前
Unity3D Huatuo技术原理剖析详解
unity·unity3d·游戏开发·性能调优·热更新
沐泽Mu5 小时前
嵌入式学习-QT-Day05
开发语言·c++·qt·学习
szuzhan.gy5 小时前
DS查找—二叉树平衡因子
数据结构·c++·算法
火云洞红孩儿6 小时前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
FeboReigns7 小时前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns7 小时前
C++简明教程(10)(初识类)
c语言·开发语言·c++
zh路西法7 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式