非射击者的运动预测
我正在开发具有中等规模的多人游戏的等距2D游戏,大约有20-30个玩家同时连接到持久性服务器。在实现良好的运动预测实现方面,我遇到了一些困难。 物理/运动 游戏没有真正的物理实现,但是使用基本原理来实现运动。状态更改(即/鼠标下移/上移/移动事件)不是持续轮询输入,而是用于更改玩家正在控制的角色实体的状态。玩家的方向(即东北方向)与恒定速度结合在一起,并转换为真实的3D矢量-实体的速度。 在主游戏循环中,在“平局”之前调用“更新”。更新逻辑触发“物理更新任务”,该任务以非常零的速度跟踪所有实体,并使用非常基本的积分来更改实体位置。例如:entity.Position + = entity.Velocity.Scale(ElapsedTime.Seconds)(其中“ Seconds”是浮点值,但相同的方法适用于毫秒整数值)。 关键是没有插值用于运动-基本物理引擎没有“先前状态”或“当前状态”的概念,只有位置和速度。 状态更改和更新数据包 当玩家控制的角色实体的速度发生变化时,“移动化身”数据包将发送到服务器,其中包含该实体的动作类型(站立,行走,奔跑),方向(东北)和当前位置。这与3D第一人称视角游戏的工作方式不同。在3D游戏中,速度(方向)会随着玩家的移动而逐帧变化。发送每个状态更改将有效地每帧发送一个数据包,这太昂贵了。取而代之的是3D游戏似乎忽略了状态更改,而是以固定的时间间隔(例如,每80-150毫秒)发送“状态更新”数据包。 由于速度和方向更新在我的游戏中的发生频率要低得多,因此我可以避免发送每个状态更改。尽管所有物理模拟都以相同的速度发生并且具有确定性,但是延迟仍然是一个问题。因此,我发出常规的位置更新数据包(类似于3D游戏),但发送频率却要低得多-现在每250毫秒发送一次,但是我怀疑有了良好的预测,我可以轻松地将其提高到500毫秒。最大的问题是,我现在已经偏离规范了-所有其他在线文档,指南和示例都会发送例行更新并在这两种状态之间进行插补。看来与我的架构不兼容,我需要提出一种更好的运动预测算法,该算法更接近(非常基本的)“网络物理学”架构。 然后,服务器接收该数据包,并根据脚本根据其移动类型确定玩家的速度(玩家是否可以跑步?获取玩家的跑步速度)。一旦具有速度,便将其与方向相结合以获得向量-实体的速度。会进行一些作弊检测和基本验证,并且服务器端的实体将使用当前速度,方向和位置进行更新。还执行基本限制,以防止玩家因移动请求而淹没服务器。 更新其自身的实体后,服务器向范围内的所有其他玩家广播“头像位置更新”包。位置更新包用于更新远程客户端的客户端物理模拟(世界状态),并执行预测和滞后补偿。 预测和滞后补偿 如上所述,客户具有自己的地位。除作弊或异常情况外,服务器将永远不会重新定位客户端的头像。客户的化身不需要进行推断(“立即移动并稍后更正”)-玩家看到的是正确的。但是,所有正在移动的远程实体都需要某种类型的外推或内插。客户的本地模拟/物理引擎中显然需要某种类型的预测和/或滞后补偿。 问题 我一直在努力使用各种算法,并且遇到许多问题: 我应该外推,内插还是同时进行?我的“直觉”是我应该使用基于速度的纯外推法。客户端接收状态更改,客户端计算出补偿滞后的“预测”速度,而常规物理系统则完成其余工作。但是,它与所有其他示例代码和文章都不一样-它们似乎都存储了许多状态并在没有物理引擎的情况下执行插值。 当数据包到达时,我尝试在固定的时间段内(例如200ms)用数据包的速度对数据包的位置进行插值。然后,我利用插值位置和当前“错误”位置之间的差值来计算新矢量,并将其放置在实体上而不是发送的速度上。但是,假设另一个数据包将在该时间间隔内到达,并且很难猜测下一个数据包何时到达-尤其是因为它们并非都按固定的时间间隔到达(即,状态也发生变化)。这个概念从根本上是有缺陷的,还是正确的,但是需要一些修复/调整? 远程播放器停止播放时会发生什么?我可以立即停止该实体,但是它将定位在“错误”位置,直到再次移动为止。如果我估计一个矢量或尝试进行插值,则会遇到一个问题,因为我不存储先前的状态-物理引擎无法说“到达X位置后需要停止”。它只是了解速度,没有什么更复杂的了。我不愿意将“数据包移动状态”信息添加到实体或物理引擎,因为它违反了基本的设计原则,并在游戏引擎的其余部分流失了网络代码。 当实体碰撞时会发生什么?共有三种情况-控制玩家在本地发生冲突,两个实体在位置更新期间在服务器上发生冲突,或者远程实体更新在本地客户端上发生冲突。在所有情况下,我都不确定如何处理碰撞-除了作弊外,这两种状态都是“正确的”,但是处于不同的时间段。在远程实体的情况下,将其拖过墙是没有意义的,因此我在本地客户端上执行碰撞检测并使其“停止”。基于上面的第二点,我可能会计算出一个“校正向量”,该向量将不断尝试将实体“穿过墙”移动,但此操作将永远不会成功-远程化身被卡在那儿,直到错误变得过高并“捕捉”到位置。游戏如何解决这个问题?