Unity游戏帧同步技术分享篇【01】帧同步解决方案概述

前言:

1.0 帧同步原理与简介

A.什么是帧同步?
帧同步是一种前后端数据同步的方式,一般应用于对实时性要求很高的网络游戏。

其基本实现流程及思路可以概括为:

1.所有客户端每帧上传操作指令集到服务器;
2.服务端将这些操作指令集保存到对应帧序列字典,并记录帧号,并在下一帧将其广播给所有客户端;
3.客户端收到指令集后,分别按帧序,帧号进行执行指令集中的操作命令。
4.也就是:相同的时机(帧序列) + 相同的操作命令(指令集-确定性) = 相同的结果(帧同步)

Unity游戏帧同步技术分享篇【01】帧同步解决方案概述_第1张图片
B.帧同步解决方案的适用范围?
通常针对于RTS[即时战略],ACT(多人实时格斗),MOBA类的游戏类型提供帧同步解决方案建议。
上述类型的网络对战游戏的特点是:实时性要求极高,追求公平竞技,打击感。
综上所述类型的游戏开发时应该优先考虑帧同步解决方案,以追求一致性。

C.帧同步解决方案内容
帧同步游戏开发中,其核心原理实现的解决方案主要有三种:

  • 目前帧同步主要处理方式为以下三种:
  • 1.0 帧锁步[Lockstep]
  • a.什么是帧锁步?
  • 当客户端A存在网络延迟导致服务器第X帧收集不到A的第X帧输入指令包时,
  • 服务器就需要等待所有客户端的X帧指令收集完成,才会下发X帧的所有客户端包数据以此保持同步,
  • 也就意味着一人延迟,所有客户端都得等,显然这种同步概念不太适用于竞技性比较高的游戏,玩家体验会很差。
  • 2.0 乐观帧
  • a.什么是乐观帧?
  • 其他乐观帧是在帧锁步的基础上进行改良,为了提高游戏体验,服务器不再要求必须收集所有客户端X帧的操作指令才进行分发,
  • 而是每个客户端都进行本地输入指令快照存储,存储各自的输入指令(并带有帧号)。
  • 如果A客户端因为延迟从第2帧(逻辑帧)开始丢包直到第5帧传输正常,那么服务器即使收不到A客户端2-5帧的输入指令还是继续分发,
  • 直到第5帧再将A客户端2-5帧的本地快照一并分发出去,同时将其他客户端2-5帧的输入指令同步到本地客户端,当然丢失3帧的数据直接在一帧同步
  • 到客户端会导致卡顿瞬移,所以A客户端需要进行加速操作执行指令操作,通常会做差值计算,通过补间动画的方式让同步更平滑。
  1. 预测回滚
    每个客户端按照帧同步的方案推进着游戏,但是如果遇到服务器没能及时返回其他玩家操作的时候,给对应的玩家预测一个操作(复制该玩家最后一次操作),并继续推进游戏,如果在其后收到了服务器玩家关于这个人的操作,则把游戏回滚到预测开始的那一帧重新计算一遍。
    《守望先锋》便采取了这种模式,获得了很大效率的网络优化。预测回滚核心在于预测写入快照,适当时机进行回滚,然而每一帧都写入快照显然内存会吃紧,同时回滚机制会变得异常复杂。
  • a.什么是预测回滚?
  • 预测回滚是一种同步机制,是乐观帧的一种解决方案,上面提到的乐观帧有个最大的缺点是一旦A客户端丢包,那么其他客户端的A在丢包期间将不会有任何
  • 操作指令同步,这样会影响游戏体验,所以这里引用预测机制,如果客户端在X帧没有指令数据那么服务器会给它在X帧一个预测的输入指令,当A客户端连接后,
  • 服务器会对预测数据和真实丢失快照数据进行和解,回滚。
  • b.如何和解
  • 下章详细介绍
  • c.为什么推荐使用ECS架构做预测回滚?
  • ECS框架可以很轻松的做到逻辑与渲染分离,所有的数据快照都存储在Component中,这将让回滚变得容易,
  • 我们只需要将每帧的Component快照进行存储,我们就可以很容易的回滚到任何逻辑帧的状态。

D.网络解决方案

E.帧同步与状态同步

  1. 两者有何区别?

  2. 状态同步方案能否替代帧同步方案?

    这里我们引入ECS架构模式:
    

2.0 ECS框架

A.ECS架构概念:
Unity游戏帧同步技术分享篇【01】帧同步解决方案概述_第2张图片
B. 基本原理:
ECS系统的特征是将状态(由Component提供)与行为(由System系统)进行分离.在Unity传统编程中,我们利用MonoBehavior来编写游戏 核心思想:将数据和行为分开。即逻辑与渲染分离 !·

1.在Unity中,我们将MonoBehavior组件放到GameObject中。但Entity系统中不同,Component被设计为附加到Entity上。·
2.使用一个pool来包含所有Entity。通过pool我们可以看到所有的entity。·
3.我们可以对entity进行分组,分组叫做group。之后我们可以通过指定的规则来区分不同的group,·
4.这个规则叫做matcher,通过matcher可以方便地快速获得指定类型的entity。

C. ECS相关链接:
游戏开发ECS设计模式初探
守望先锋 网络架构设计分享
Unity ECS简介
UnityECS高性能探索

**

3.0 定点数

1.什么是定点数?为什么要使用定点数?

定点数是指在计算机中小数点的位置固定的数。相反小数位数不固定的则是浮点数,例如float,double数据类型,它们属于高精度单双浮点数类型,由于不同机器CPU对浮点数的处理结果会存在偏差,再加之网路延迟影响,由此而带来的蝴蝶效应影响,终会导致不相同的计算结果,导致逻辑不可控,因此为了数据的完全的一致,应采用定点数替代浮点数。

  • 由此我们发现帧同步网络服务器只负责收发指令数据,不进行战斗逻辑的计算。那么由于客户端性能算力不一样,导致浮点数数据类型(float(单),double(双))不再适用,
  • 因为浮点数带有精度,会导致每个客户端的计算结果会存在偏差,从而导致不同步,进而产生蝴蝶效益导致正常战斗完全不同步。所以这里引入定点数。
  • 定点数专为解决浮点数精度差,主要实现方式有两种:
  • a.尾数截断
  • 可以到这里去看看这里帧同步项目的实例,注明:这是早期Photon的TrueSync经过大佬完善后的实例,有参考价值,其中用到就是[尾数截断]实现定点数
  • b.移位运算
  • ps:
  • 左移操作时将运算数的二进制码整体左移指定位数,左移之后的空位用0补充
  • 右移操作是将运算数的二进制码整体右移指定位数,右移之后的空位用符号位补充,如果是正数用0补充,负数用1补充
  • 通过移位操作我们可以消除精度,至于移位数我觉得(16位)已经完全可以满足需求。
  • 注:65536是右移位16的结果
  • 将浮点数右移16位得到一个long类型,long类型赋值给Fixed类型编码,便得到一个定点数。
  • var l= (long)unchecked(value * 65536f)
  • public static Fixed FromRaw(long value)
    {
    Fixed r;
    r.RawValue = value;
    return r;
    }
  • 同样将反向转换只需左移位操作即可:
  • public static explicit operator float(Fixed value)
    {
    return (float)value.RawValue / 65536f;
    }

这里为大家提供一个定点数库(采用第一种方式,通过移位运算精确):
帧同步开发-【标准定点数库】[FP] [FPMath] [FPMatrix2x2] [FPQuaternion] [FPVector2][FPVector3]

下一篇博客,Unity游戏帧同步技术分享篇【02】帧同步物理引擎解决方案

感谢你的关注!

你可能感兴趣的:(unity3d,帧同步,定点数,游戏,github,unity)