案例游戏:
《英雄联盟》 《王者荣耀》等PVP游戏
实际解决方案:
同步机制:不锁步的帧同步
网络:传输层的UDP配合应用层的可靠性检验
以下从问题出发讨论解决方案。
主要问题:
1. 性能
1) 服务端性能:具体表现为延迟,影响用户体验。
2) 客户端性能:具体表现为卡顿。
总结下来,与任何C/S应用一样,性能由服务端计算效率+网络延迟+客户端计算效率三部分决定。
其中,网络延迟我们只能在socket上进行改进,也就是只能在传输层以上想办法优化。数据链路层和物理层以下的效率问题不是应用开发人员能够解决的,只能寄希望于未来基础设施和通信内核的改进。
那么性能问题就成了在客户端和服务端的计算量负载上找到一个平衡点。
2. 安全
1) 追求低延迟下的数据完整性和数据一致性,保证游戏不能有逻辑上的bug。
2) 防作弊:本质上还是解决数据一致性问题。
分布式自然会带来一致性问题,把大部分计算放在客户端,即每个人的终端上,服务端的压力就会越小,但是同时越难保证一致性。
这也是《吃鸡》这一类多人FPS游戏外挂众多的原因——对局人数多,计算量大,只能把很绝大部分计算都放在每个玩家电脑上完成。
不止游戏应用,任何分布式计算都需要解决一致性问题。
可选方案:
1. 中心化MMOG模式
最容易想到的,以开发网站或C/S应用的经验来说,客户端提交操作数据,服务端统一计算游戏逻辑,再更新玩家状态到客户端。如《梦幻西游》等MMORPG回合制游戏中,游戏实时性不强。
1) 安全性强
因为服务端掌握了一切数据和计算,客户端实际上只是一个显示平台。这种方式可以保证数据的一致性和完整性,反作弊能力强。
2) 服务端压力大and开发难度大
因为安全性而牺牲了性能,服务端复杂度随着玩家数量规模的增大而指数上升。回合制游戏还可以考虑,因为客户端和服务端都可以保证同步,面对MOBA这种实时性强的游戏,服务端在游戏对局中的角色成了一个上帝视角的“裁判”,必须收集所有玩家的实时操作之后,完成 “校验” 和 “计算” 两个环节。从开发角度来说,逻辑压力更大。
3)同步带来的阻塞
对局中如果有某一个玩家网络延迟大,服务端会阻塞在收集所有操作的过程中,所有玩家都要被迫等待,如木桶效应,游戏体验此时由网速最卡的那个玩家决定。这也是MMOG模式不适合实时对战的原因。
此时, 在MMOG模式中, server和client的角色为:
服务端: 负责收集玩家操作并完成所有计算;
客户端: 从服务端获取数据并完成展示.
举例:
A对B进行了一次攻击,A把一次攻击的请求发送到服务端,服务端维护当前A和B的状态,计算攻击的伤害,更新B的状态,最后分别返回到A和B的客户端中,A玩家和B玩家分别在终端上看到自己攻击与被攻击后的状态。
2. 帧同步
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NLjQ7aqH-1583685555501)(])
因此改变了server和client的角色,
服务端:
1)维护一个全局的时钟,以“帧”的形式保证每个客户端对局中处于游戏的同一时刻。(一秒内应有30~50帧)
2)负责收集所有玩家的操作,但不进行计算,只负责广播到对局之内的其他玩家的客户端中。
客户端: 完成对所有玩家操作的校验和游戏逻辑的实时计算。
帧同步就像其他玩家都通过网络联机到了你的手机或者电脑当中,你的客户端再根据玩家们的操作在本地进行运算。
1) 性能提升。
服务端的任务减轻了许多,足以支持实时对战。性能优化的瓶颈主要在客户端。
2) 安全性降低。
帧同步机制控制数据一致性的原理在于保证同一个输入在不同的客户端中都能得到同一个输出。 如果破坏了这个前提,那就是外挂的雏形。比如锁血挂,血量扣除的计算在本地运行,把计算逻辑改了的话,也就是完成了开挂了。
因为游戏计算任务在本地进行,作弊的难度下降了许多。
3)开发逻辑难度下降。
这样就变成了一个以玩家为主角,同时伴有队友和对手操作的类似单机游戏的逻辑,开发时不再是一个掌握全局的“裁判”的角色,只需站在玩家的角度上考虑释放技能或受到伤害等事件发生时的业务逻辑。
4)“等时不等待”。
按照严格的帧刷新,没有收到玩家操作即为空。这也是掉线之后的玩家站着不动的原因。也会带来一些不影响公平性的不同步,比如网络差的情况下,我们会看到自己的角色一直跑出地图边际。
举例:
A对B进行了一次攻击,服务端在该时刻收集了操作广播到所有对局玩家客户端中,各个客户端播放A攻击的技能动画,B客户端对A攻击的伤害进行计算,扣除血量之后更新状态到各个客户端。
锁同步仍存在的问题:
1. 掉线重连困难
因为只有自己的客户端保留了自己的游戏状态(属性,等级等),服务端并不维护游戏状态,因此一旦掉线,重连难度大。就像队伍整齐划一地走着左右左的步伐,一旦掉队,想要跟上节奏很困难。
解决方案一:
定时保存状态,但会一部分退化成MMOG模式,服务端压力会变大。
解决方案二:
将游戏进行过程中的操作保存,重连时需从服务端重新读取所有操作,自行演算到当前时刻的状态。玩家等待时间长。