玩家角色的客户端预测与服务器对账
问题来源
在多人游戏中,外挂作弊不只是让自己的游戏体验更好,还会让其他玩家的游戏体验更差,因此在开发过程中需要避免这种情况的发生。
权威服务器和哑客户端:为处理外挂问题,游戏中的逻辑通常只由服务器处理,客户端只负责显示,一切以服务器为准。
此方案在慢节奏的游戏中运行良好,但遇到快速刷新的游戏就要考虑网络延迟的问题。比如考虑一个实际场景,客户端发出指令向前运动一米,指令需要传给服务器(花费100 ms),服务器处理后返回角色运动一米后所在的位置(花费100 ms),再由客户端在画面上表现出来。因此在玩家发出指令后将在200 ms内游戏画面没有任何变化然后突然向前运动了一米。这种滞后对游戏来说当然是灾难性的。
客户端预测
- 通常游戏世界是具有足够确定性的,即给定一个游戏状态和一组输入,结果是完全可预测的。因此基于此确定性,我们可以在客户端发出指令之后在等待服务器回应期间就开始呈现效果,这样既保证了服务器的权威性,又能消除表现上的割裂感。
- 以上方案看似已经完美解决了问题,但考虑一下当玩家的连续操作时间间隔小于网络延迟的时候就会出现问题。比如:玩家连续按下两次向前移动一米的操作,移动所需时间为100 ms,但网络延迟有250 ms,这就会导致在200 ms时角色在前进两米的位置,但250 ms时收到服务器的第一次消息强制更新回到前进一米的位置,而在350 ms时又会更新到前进两米的位置。这一问题就需要服务器对账来解决。
服务器对账
- 在客户端向服务器发送请求时加入一个序号,当服务器回复时,包括它处理的最后一个序号。借此客户端可以根据服务器发送的最后一个权威的状态以及服务器未处理的输入计算游戏当前的状态。
多人游戏其他玩家:实体插值和滞后补偿
多人游戏带来的新问题
- 服务器时间步进:当有多个客户端同时高频次地向服务器发送请求时,每次输入都更新游戏世界会消耗太多的CPU和带宽,因此通常会对客户端的输入进行排队,设置一个服务器步进时间,如100 ms,在该时间段内对所有收到的操作进行统一处理并广播给客户端。
- 客户端处理低频更新:对于玩家自己控制的角色来说处理和前文所述基本一致,能靠预测填充服务器两次消息间信息的空白,但是对于其他玩家控制的角色来说,获得的信息就会比较稀疏,如何在数据有限的情况下对其他玩家控制角色的运动进行平滑就是新的问题。
Dead reckoning 死亡计算
在某些游戏如赛车游戏中,客户端可以获取权威的速度、加速度等运动参数,在下一次服务器数据到来之前,假设在此期间汽车不发生运动参数的突变(如发生碰撞、用户输入等),客户端根据当前参数预测汽车的运动,在服务器数据到来之后进行更正。
限制:在某些情况下,死亡计算完全无法使用,例如在射击游戏中,玩家的方向和速度是可以立刻改变的,此时,玩家以较高的频率和飞快的速度进行转向、加速、停止,因此,完全无法根据当前的运动参数预测接下来的运动。
实体插值
- 另一种解决方法是实体插值,例如,在t=1000 ms时,客户端已经收到了来自服务器t=900 ms和t=1000 ms的可靠数据,在1000 ms到1100 ms期间,客户端就显示对900 ms和1000 ms插值之后的数据。在此种解决方案下,插值获得的显示效果是流畅且足够准确的,唯一的问题是客户端显示的是100 ms前的数据。
滞后补偿
- 最后需要解决一个问题:实体插值带来的延迟。当下,玩家看到的实际是自己当前的位置状态和100 ms前其他玩家的位置状态。在某些情况下,这100 ms的延迟也是致命的,如:你向着敌人开枪,但你瞄准的实际上是100 ms前的敌人,此时敌人的位置已经不在你看到的位置。
- 解决方案:当玩家按下射击按钮时,客户端会向服务器发送当前事件,包含准确的时间戳和目标。服务器接收到数据后会根据时间戳重建过去的世界,处理射击的结果并发送给客户端。唯一的问题是敌人有可能在掩体后被杀,因为他实际上是在100 ms前运动到掩体前被杀的,但做了延迟处决。
参考:
Client Server Game Architecture
Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization