【参考博文】GAD-网络游戏同步技术
帧同步的形式很泛,根据不同游戏,使用的技术范围又不一样,所以大家都在讲方法论,要全面覆盖可能需要较大的篇幅,所以,我简单描述下。
假定大家对帧同步和状态同步有一定的认识,理论上的问题,我就不作过多解释了。
大家都知道,帧同步的核心是一致性,通过一致性的算法,使得各端输入一致的情形下,输出也是一致的,以此,可以解决同步的根本问题——仲裁的结果是一致的。
严格的帧同步,实现起来,解决算法一致性、输入一致性的问题基本就能实现了,然而实践过程中,由于网络延时、抖动、用户实时体验带来了一些其他的问题,使得问题解决起来变得更复杂,为了把问题聚焦,这部分内容我们就不重点讨论,后面的篇幅,主要聚焦在如何保证算法一致性和输入一致性。
算法不一致的主要原因是数值计算不一致和算法流程不一致。
数值计算。
浮点数的运算精度,在不同机器上有不同的表现,由此,导致了浮点数的精度可能导致计算结果不一致。
所以,首要问题是把游戏逻辑部分(后续会有关于逻辑和表现分离的说明)所使用的浮点数运算全部改成具有一致性的数值运算。
那么,怎么实现一种数值运算,既具有小数的表达能力,又有一致性的运算结果呢?
常规方法中,尽管形态多样,但理论来讲,基本都是定点数(关于定点数的实现,可以翻看其他资料)。
工程上,还要考虑定点数的精度,不同的精度,性能可能不一样,如何在精度和性能之间做平衡,需要结合自己的数值范围来确定。
综上所述,我们导出需要实现的代码。
实现1:定点数
包含浮点数计算的常规算法,包含加、减、乘、除、绝对值、负运算等基本运算,另外,还要根据自己的使用实现开平方、指数函数、对数函数,三角函数等。
其中定义域较大的函数,一般使用解方程的方法,比如牛顿法,定义域较小或者可以限制到一个周期以内的函数,比如三角函数,定义域在0-2pi,可以使用泰勒级数,具体用几阶,要根据自身对精度的要求,同时,由于有限项的泰勒级数只在某些定义域区间较好的拟合度,所以需要在通过数值分析来论证精度。最后,其实三角函数可以通过查表法来实现,性能快,精度与所需要存储的表格大小有关系。
至此,我们拥有了一致性的定点数,我们用它来替换浮点数,然后在有些应用场景,逻辑层也会使用到更多的数学工具,比如向量、矩阵、欧拉角、四元数等,所以我们得实现这部分数学工具。
实现2:一致性数学工具
1. 向量、矩阵、欧拉角、四元数
2.几何工具:点、线、面、体,各种几何元素的关系,相交性检测(做一个射击游戏,需要射线检测来判定是否命中),不是所有的都要写,用多少实现多少。
除了上述的内容,还有逻辑层可能用了物理,物理部分主要包含碰撞检测和动力学
比如抛出一个篮球,带抛物线的,需要一致性的物理运算,篮球碰到框则需要一致性的碰撞检测,碰到框之后的反馈也可能需要动力学表现(这部分可以是表现层)。
实现3:一致性物理系统
包含逻辑层需要使用的动力学和碰撞检测
有些游戏逻辑需要动画系统支持,比如动作游戏,一个技能触发一个动作,动作除了表现层的动画(比如2D游戏的图片序列),还有一些影响逻辑的部分,比如动画控制了攻击判定框在时间轴的变化,这个时候需要实现逻辑层上的动画,主要需要解决的问题是,时间要改成整数或者定点数,另外,插值数据的类型也得使用一致性得数据类型,比如位置向量等。
实现4:一致性动画系统
与常规得表现层动画类似,可以根据需要简化。(比如不需要骨骼或者不需要融合)
数值一致性的常规问题大致说这么多,还有些其他的内容,比如寻路,AI判定等凡是有浮点数的地方,全部换成定点数,最终还是需要根据项目需求,选择需要实现的内容。
注意:定点数的运算性能比浮点数差很远,数量级在10-100以上,需要评估性能,好规划策划设定的问题规模。
前面讲到,算法不一致的另外一个原因是:流程不一致。
流程不一致的主要原因,可能是输入导致的,也可能是架构导致的。
输入导致的算法流程不一致,这个问题,我们把它归到输入一致性,后面讨论。
主要讨论架构导致的流程不一致。
首先,帧同步需要在架构上做逻辑和表现分离,那么,什么是逻辑,什么是表现,其实没有绝对的定论,一般来说,对游戏结果有影响的部分为逻辑,对游戏结果没影响,只影响视觉、听觉、和其他与游戏结果无关的用户交互的部分为表现。
整个帧同步的运行体系,简单的模型历程流程大致为:
游戏框架跟从网络收取同步包->上传用户输入->分发同步包给逻辑内核(用户输入)->逻辑内核更新->逻辑内核发送消息给表现层->表现层更新
需要注意的几点:
1.逻辑更新频度和表现更新频度不一致
2.逻辑内核更新的时间片是等间隔的,表现不一定
3.同步包的内容是所有用户在某个时间片以内的输入
容易出错的地方是,逻辑层和表现层交织在一起,表现层影响逻辑层,导致算法流程不一致。
所以,帧同步实施的第一要务是明确分离逻辑层和表现层。
完了算法计算一致性,接下来,我们要讨论,输入一致性的问题。
这里所说的输入比较笼统,整理讲的是逻辑层的输入,包含一些全局变量、随机数的种子等等,最重要的一个问题是,逻辑层每帧的时间间隔是固定的,没有特殊需求的话,建议用整数表示,精确为毫秒。
一致性的细节问题还有很多,先大概讲这么多吧。
解决了一致性问题,一个严格的帧同步模型,后续实施就相对轻松了。
强调下,为什么反复提“严格”的帧同步模型,因为实际运行过程中,很可能出现不严格的模型,当网络延时较大、抖动、以及游戏对输入响应有更高要求的时候,需要做一些预先的表现(表现层的预测),当结果预测不一致的时候,需要回滚,这个跟状态同步差不多,当然也有逻辑层的预测,算帧同步的高级应用,目前少有游戏使用。
最后,说下回放、断线、反外挂、注意事项
帧同步实现回放有天生的技术优势,因为有了一致性的保证,存储初始数据和用户输入,逻辑内核启动播放模式,将时间轴的用户输入,按照时间轴消耗即可。
断线其实是回放的变种,应用形态不一样,技术差不多,可以不驱动表现。
反外挂,帧同步的游戏结果是所有客户端一起说了算,算法一致性保证了大家的结果是一致的,可以简单使用少数服从多数的方式来判定谁用外挂,两方数量一致(或者差不多)的情形,可以使用部署在仲裁服务器上的逻辑内核来计算结果,以服务器为准。
注意事项
帧同步适用的情形:参与玩家数量不多(人数影响单个同步包的大小)
有利情形:各端需要同步的NPC数量众多、游戏逻辑运算量很大
大概就这么多,表达能力有限,感觉也没讲清楚,只是希望对你有所帮助。