stonstad 发表于 2021-12-17 17:13

Unity3D高级编程网络同步解决方案

当前网络游戏中网络同步方案有三种,即状态同步,实时广播同步,帧同步。三种方式并不互相排斥,它们可以混合使用。很多时候我们在开发的时候,为了能都让游戏显得更加逼真,会选择多种的同步方案一起使用。例如魔兽世界这种开放世界的多人在线RPG游戏,起初它们就使用了状态同步和位移信息同步两种方案,绝地求生、和平精英等战地竞技类游戏,也同样使用了状态同步与实时广播同步方案,而传奇世界、热血传奇等传奇类游戏因为有严格的寻路同步机制所以就只使用了状态同步,而王者荣耀、英雄联盟等一批5v5地图类竞技游戏则更多使用了帧同步方案。
===
同步方案的目标是在针对多人游戏中如何用更少的信息同步量来逼真的’模拟‘其他玩家的一举一动,让我们在玩游戏的时候能知道并看到其他玩家的位置、动作、及状态。这里的关键词是’模拟‘,我们本地设备中获取的信息由于网络因素的关系通常都是延后的,如何通过这些延迟的信息来模拟真实角色的位移、动作、特效是整个方案的关键。
在同步的解决方案中不仅涉及到信息同步还涉及到同步的范围,如果我们将每个玩家每次的变化信息向游戏内的所有玩家都广播一遍,那么我们需要同步的数据量太大,这不仅客户端承载不了这样大量的渲染压力和信息通信压力,服务器也同样无法承受如此巨大的数据传输压力(会占满服务器网络带宽或让网络费用过大)。因此同步方案对游戏来说不仅仅只是用来逼真模拟其他玩家的一个解决方案,它还需要解决和优化网络传输数据带来的压力和部分渲染压力。下面我们就来讲解下三大网络同步解决方案的方法论。
状态同步法
为什么要状态同步?同步机制主要目的模拟,倘若我们每帧(以每秒30帧为标准)都将自己的信息同步给所有人,那么需要传输和广播的信息量就太大,因此我们需要尝试更节省流量的方式。游戏角色通常可以用状态形式来代表某一段时间的行为,因此如果我们使用状态信息作为同步信息广播给其他设备去模拟的话就会比较节省流量,这样当我们收到同步的广播信息后,就可以在一段时间内不需要其他信息就能模拟出这些角色的动作、位移、以及特效。
为了能更逼真的模拟其他玩家的行为,我们把每个人的行为方式抽象成若干个状态,每个状态都有一套行为方式将,有时尽管3D模型不一样但所调用的程序是一样的。比如空闲状态,所有人都会站在某个位置循环播放站立动画,这时我们只要告诉玩家说我在某个位置进入了空闲状态,只要我们的状态不变,其他玩家就可以在不收到任何同步数据的情况下知道我就是一直在原地并循环播放着站立动画。当然这是最简单的状态,我们也可以有其他比较复杂的状态,比如包括攻击状态、追击状态、防御状态、奔跑状态、技能状态、寻路状态等。
在状态同步里,角色身上每个状态就相当于一个具有固定逻辑的行为模式,这个固定行为模式就像个黑盒,只要给到需要的数据,就能表现出相同的行为,比如攻击状态,就会播放一个攻击动画并在某个时间点判定攻击效果,攻击动画完毕后结束攻击状态。比如打坐休息状态,就会循环播放一个打坐动画,并每隔一段时间恢复一次血量。又比如寻路状态,就会从某点到某点做A星的寻路计算,边播放行走动画边跟随路线向各个路线节点移动,最终到达到目地完成寻路状态。最复杂的应该属于技能状态,技能有很多种很多个,每种技能都有不一样的流程,同一种技能还有不通的动画和特效,一些复杂的技能更是需要配备复杂的逻辑,但有一点是可以肯定的也必须做到的,那就是向技能状态输入相同的数据应该展示出相同的表现,例如向技能状态中,输入火球技能ID,目标对象,施法速度,角色在收到数据后就需要展示火球技能的动画,在等待吟唱时间后向目标发射火球。
这些状态都有一个共同的特点就是只要我们给予所需的相同的数据就能展现出相同画面的个体效果。现在我们要让这些状态连贯起来拼凑成一个拥有一系列动作的角色,当我们向这个角色发送各种各样的指令时,就是在告诉它你应该触发这个状态然后触发那状态,由于指令中包含了状态需要的数据,这些数据广播给每个需要看到的玩家,收到这些状态信息的设备就需要通过这些同步数据去模拟角色的行为,从而让画面看起来像是很多玩家在操控自己的角色。
在状态同步中,我们说的通俗点,服务器端扮演了幕后操纵木偶人的那个大老板,而客户端里渲染的对象就是那个木偶人,服务器端发出指令说ID为5的木偶人开始攻击,客户端就执行它的指令,找到那个ID为5的木偶人并开始进入攻击状态的相关逻辑。当动画播放到一半服务器端又发来指令说,被攻击的那个ID的怪兽受伤了并受到500点伤害,这时客户端就会在指定的怪物头上冒出500点的伤害值并且让怪物进入受伤状态播放受伤动画。
现在当攻击动画播放完毕时,服务器又发来指令说继续攻击,客户端又根据这个指令找到ID为5的木偶人将它从站立状态切换到了攻击状态,不过这次攻击状态快结束时服务器发来指令说,这个怪物受到600点伤害并且死了,于是客户端根据这个指令找到这个怪物并在头上冒出600点伤害的数字,然后让怪物进入死亡状态播放死亡动画。木偶人也在播放完攻击动画后,进入了空闲状态循环播放了站立动画。
服务器扮演着发送指令操控木偶人的角色,这个木偶人也包括玩家自己的角色,即当玩家操控’我‘自己的在游戏中的角色时,也同样遵循经过服务器的同意并发送指令给玩家的过程,当玩家收到同步信息指令时当前的角色才会进行状态的切换和模拟。
不过也未必要一定要经过服务器同意才做模拟渲染,为了让玩家能够在网络环境糟糕的时候也能够看起来比较顺畅,我们在制作网络同步逻辑时,让玩家可以随意操控自己的角色,并不受限于服务器的延迟指令,但是在稍后的服务器校验时再对玩家进行矫正,最明显的例子就是我们在玩传奇类游戏中时的状态同步,在网络环境不太顺畅的情况下我们任然能操作自己的角色不停的移动,但在过一段时间后,当客户端接收到服务器发来的正确的数据后,客户端对角色进行了位置和状态的矫正。
状态同步的是根据角色拥有不同的状态,每个状态在某一段时间下的行为可以根据数据被预测,除非状态被改变,否则相同的状态数据得到的是相同的个体状态结果,这样角色实体的行为就可以通过状态切换来模拟表现画面。
实时广播同步法
在一些FPS类型的竞技游戏中,人物的行动速度和旋转速度在不断的变化而且频次比较高,如果想要模拟不同玩家在游戏场景中的位置与旋转角度就要实时更新这些信息,这时状态同步就不能满足这样需求,由于我们移动的速度和旋转的变化太快频次太高,因此无法做到拆分同步状态来模拟。不过状态同步依然可以用于除了移动和旋转意外的同步方式,因为除了移动和旋转,其他信息都没有这样快速、多样的变化,并且角色很多数据仍旧遵守状态的规则,对于这些数据我们可以继续用状态来划分,因此状态同步常常与实时广播同步同时存在。
实时广播同步方案的主要特点是,位置和旋转信息由客户端决定。客户端将自身的位置、旋转信息发给服务器端,再由服务器端分发给其他玩家,当其他玩家收到位置、旋转信息后根据收到的位置和旋转信息预测其当前的位置、速度、加速度,以及旋转速度,旋转加速度并进行模拟和展示。
在这种竞技性比较强,移动速度比较快的游戏中,通常都需要玩家不停的改变移动速度和旋转角度来体现其控制角色的灵活性。比较常见为枪战类游戏CS,玩家不停在变化自己的位移速度和旋转角速度以适应战斗的需要。除了FPS类型的游戏,赛车类游戏也是类似的情况,在跑跑卡丁车游戏中,玩家要在高速移动下,不停的调整自己的方向和速度,让自己能够躲过众多障碍,同时在急弯处要旋转自己的车进行漂移等。魔兽世界也会使用实时广播同步法,这种开放世界下的RPG游戏,需要不停的改变自己的速度与旋转角度来让战斗显得更加丰富和灵活,不过魔兽世界在实时广播之上加了些验证机制让客户端不能为所欲为的决定自己的位置。
为了能更加逼真的同步模拟这种变化频率很高的人物移动和旋转,我们不得不让客户端来决定其位置和旋转角度,这将牺牲一些数据的安全性来让画面模拟的更加真实流畅。
每个玩家设备上的客户端会在1秒内向服务端发送15-30次左右自身的移动和旋转数据,为的就是让其他玩家在收到广播数据时能更加顺畅的模拟玩家在游戏中的移动旋转的表现,也只有这样才能让其他的游戏客户端不停的更新玩家的位置、移动速度和旋转角度。不过只是单纯的更新位置和旋转数据,会导致玩家在屏幕中不停的闪跳,因此我们用速度的方式表示它们的移动方式会让角色模拟运动得流畅些。当我们收到广播的玩家实时数据时,先计算速度和预测速度,以及加速度,让模拟的对象按速度和加速度的形式在屏幕中运动,而不是只更新位置,这让角色在画面中模拟行走的位置和方向时更加流畅。
实时广播同步的算法和公式并不复杂,首先要取得已经收到的该玩家的位置信息前5个除以间隔时间,就能得到一个平均的速度,再取这样5个一组的3-5组,就能得到一个平均的加速度,根据这个速度和加速,就能让角色在屏幕中模拟出相对准确的跑动位置、速度和方向。不只是速度和加速度,我们还需要角色当前的面向的角度,以及旋转的角速度,在角度的同步上也可以按照这种速度和加速度的方式去预测,取最近5个角度的值得到平均旋转速度,再取5个一组的3-5组这样的数据计算得到旋转加速度。
参考文章:https://www.yxkfw.com/thread-70098-1-2.html
页: [1]
查看完整版本: Unity3D高级编程网络同步解决方案