在客户端和服务器上都运行物理模拟?


13

我正在实施一个多人小行星克隆,以了解游戏中的客户端/服务器网络架构。我花了一些时间阅读GafferOnGames和Valve关于他们的客户端/服务器技术的出版物。我在两个概念上遇到麻烦。

  1. 目前,我有一个权威的游戏服务器,使用box2d模拟物理并将每秒大约20次的世界状态发送给客户端。每个客户端都跟踪接收到的最后几个快照,并在两个状态之间进行切换以使Sprite的移动变得平滑。但是,它并不那么顺利。可能会平滑一段时间,然后有点生涩,然后恢复平滑,等等。我尝试过TCP和UDP,两者大致相同。知道我的问题是什么吗?(注意:我首先为单个玩家实现了此功能,当每秒仅更新20次物理世界时,精灵运动在60fps时非常流畅)。

  2. 为了解决第一个问题,我认为客户端也应该运行box2d模拟,并仅在不匹配时更新其sprite的位置以匹配服务器快照。我认为这可能会更流畅,因为我的单人游戏实施很流畅。这是一个好主意吗?

    即使不能解决上述问题,客户端预测是否也有必要?例如,如果玩家试图移动自己的飞船,他们如何知道是否在没有物理模拟的情况下撞到小行星,墙壁或敌方飞船?似乎他们的飞船似乎通过了应该与之碰撞的物体,然后才从服务器收到快照,说他们击中了该物体。

谢谢!

Answers:


10

绝对在客户端和服务器上运行模拟。其他任何东西都有太长的延迟。您应该通过按固定顺序插入对象并以相同顺序插入对象并避免指针比较来确保模拟匹配。我没有用Box2D尝试过这种方法,但是通常可以在物理模拟中的所有机器上实现相同的行为。所有数学通常都基于IEEE 754 binary32浮点数,并且它们的行为是严格针对操作(例如+-*/仅举几例)定义的。您需要注意一点sincos之类的工具很难,因为它们在运行时之间可能会有所不同(在为多个平台进行开发时,这一点尤其重要)。还要确保对编译器中的浮点优化使用严格的设置。您仍然可以通过定期从服务器发送对象状态来同步对象。除非差异大于阈值,否则请不要进行更新,以避免不必要的卡顿。

我想到的一个问题是新对象的创建以及这将如何改变客户端之间的模拟。解决此问题的一种方法是让服务器创建所有对象。如果当前时间步为t,则服务器将在计划要添加的对象t+d。因此,可以在所有客户端维护带有添加对象和添加对象的新对象列表,并由服务器很好地对其进行更新。如果d足够大,则可以最大程度地降低产生不同结果的风险。如果您确实无法处理差异,则可以在模拟该时间步之前强制客户端在特定时间步之前等待有关新对象的信息。


感谢您的答复。我不认为box2d可以保证在不同的CPU上产生相同的结果,这是我们编写桌面游戏的情况。我希望这些差异可以是微小的,并且可以通过权威服务器的定期更新轻松纠正,但是我从未尝试过。
Venesectrix

艾琳卡托认为,这试图保持同步多重的Box2D世界的整个状态是败局(box2d.org/forum/viewtopic.php?f=3&t=8462
帕维尔

语句“ IEEE 754 binary32 floats [..] behavior严格定义为诸如+-*/”之类的操作,这是完全错误的。IEEE-754中的所有这些操作可能会根据实现方式而有所不同。有关更多信息,请参见此处此处
BlueRaja-Danny Pflughoeft 2014年

1
不,这是完全正确的。您的链接描述的问题与x87 fpu的不同模式和超越实现有关。IEEE 754 binary32 为基本操作严格定义的。您可以自行设置正确的模式并使用正确的说明,以便遵循标准。简单地使用SSE指令而不是x87 fpu会有很大帮助。
rasmus 2014年

4

看起来好像不太好,因为在它们之间进行内插依赖于始终要插入下一组数据。这意味着,如果出现短暂的滞后尖峰,一切都必须等待以赶上。

GameDev上一篇旧文章,内容涉及使用三次样条曲线来预测对象的位置,使其超过您上一次拥有该数据的点。然后,您要做的就是使用该位置,然后在获得新数据以调整其新位置时调整样条曲线。它可能也比进行第二次物理模拟便宜得多,并且这意味着您不必决定要信任谁,因为您已经明确实现了客户端,并且可以逐步实现。:)


可能是这种情况。我要尝试的是延迟直到从服务器收到3张快照。在这一点上,我从镜头1射到镜头2,然后从镜头2射到镜头3。如果在任何时候我错过了一个数据包,我都可以从1到3而不是从1到2进行包if(如果有道理)。我可能尚未正确实施此操作。感谢您的文章链接!
Venesectrix

1

我本人也做过类似的事情,并且仅在客户端上运行Box2D。我这样做的方法是让客户端自行运行自己的模拟,将每个同步数据包上的当前速度(和旋转)发送到服务器。然后,服务器将此信息发送给其他播放器,其他播放器将新接收的速度设置为复制的实体。这非常顺利,客户之间没有任何明显的差异。

当然,这里的问题是没有对实体的集中控制,但是我认为也可以通过在服务器端进行物理模拟来在服务器端完成


感谢您的输入。我们将需要集中控制以防止作弊,因此我们必须让服务器至少运行一个模拟,才能知道客户端所说的事情是否可行。
Venesectrix

1

我个人更喜欢只在服务器上运行模拟,并让其广播所涉及对象的线性/角速度/加速度的任何变化。也就是说,当某个对象出于任何原因更改其任何物理属性(例如上述速度和加速度)时,此特定更改将从服务器发送到客户端,客户端将更改它的一侧。对象的数据。

与当前的实现相比,它的优点是它将使客户端插值的必要性消失,并将在对象上产生非常真实的行为。问题是这种方法非常容易受到延迟的影响,而当玩家在地理位置上相距太远时,这将成为一个很大的问题。

关于问题1,我说问题可能是延迟的波动,因为不能绝对保证每次接收快照之间都会有一个完美的20秒间隔。让我举例说明(以毫秒为单位的时间为“ t”):

1)自游戏开始以来,t = 20时,客户收到快照,并成功且顺利地进行了插值。

2)在t = 40时,服务器与客户端之间存在延迟,并且快照恰好实际上仅在t = 41时到达。

3)在t = 60时,服务器发送了另一个快照,但是由于延迟,模拟的一秒钟浪费在客户端。如果快照在t = 60到达,客户端将不会对40和60瞬间进行插值,而是实际上从41到60瞬间进行插值,从而产生不同的行为。这种不精确可能是最终“怪癖”的原因。

对于问题2,如果您实现一些可以有效地跟踪每个对象是否真正由客户端-服务器同步而不必发送包来通知对象位置的框架,那么您的想法可能会起作用。即使您离散地执行此操作,您不仅会遇到问题1的相同问题,而且还会传输过多的数据(这是一件坏事)。


我不确定我是否遵循您在第一段中所说的话。如果模拟仅在服务器上运行,并且您仅广播速度/加速度的变化,那么客户端如何知道应在何处绘制精灵?客户必须基于接收到的速度/加速度来模拟对象,以便正确绘制它们。我认为您可能不按我期望的时间间隔接收快照是正确的。知道如何处理吗?
Venesectrix

客户知道对象的初始位置和当前位置,速度和加速度,并会相应地更新它认为对象是独立于服务器的位置。服务器最终将通过消息更改客户端上的此类属性,因为服务器是在进行物理和碰撞检测(这势必迟早更改给定对象的速度/加速度和方向)
UBSophung
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.