与全状态更新相比,我如何更有效地同步多人游戏状态?


10

我之前已经做过一些游戏网络编码,但是主要使用TCP来实现无实时需求的游戏。我正在使用网络多人游戏开发2D Java游戏。为了学习,我想自己做,而不需要现有的网络API。

如何有效表示从服务器发送给客户端的游戏状态?有一种最明显但可能效率最低的方法,该方法是使用每个玩家的位置,动画状态等创建某种游戏状态上下文对象,并将每次更新发送给每个玩家。这似乎很难实现,但可能太大而无法实现接近实时交互的功能(当然,我的经验有限,因此我可能是错误的)。

你们中的任何人以前是否曾使用过可靠的方法来仅传输状态变化,并且在性能上是否存在足够大的差异以值得进行额外的工作?


2
尝试每帧事物的完整状态,如果它太慢(对于一个简单的2D游戏,它可能足够有效),然后尝试进行优化。如果工作正常,则工作正常,除非您以后发现网络成为瓶颈,否则不必更改它。
罗伯特·鲁哈尼

Answers:


10

定期传输完整的游戏状态通常不可行,尽管它确实很大程度上取决于游戏的复杂性。对于具有小世界模型的简单游戏,它可能会起作用。

我个人使用以下模型取得了更大的成功:

  • 游戏状态存储在空间数据结构(例如八叉树)中定义明确的对象模型中
  • 游戏状态的所有更改(无论是在客户端还是在服务器上)都描述为事件。事件可能是游戏对象的属性更改,地图图块的更改,游戏对象的移动等。
  • 随着游戏的进行,服务器上的游戏引擎会产生事件流。这些直接应用于服务器的游戏状态。
  • 事件也将发送给玩家,但前提是该事件与该玩家相关(例如,从当前位置可见该事件吗?)
  • 玩家可见性的变化还可能导致在玩家移动时“揭示”地图的新部分等事件。这也可以用来确保玩家在首次加入游戏时获得有关游戏状态的准确初始视图。
  • 玩家收到的任何事件都会更新其游戏状态。因此,它仅具有游戏状态的部分模型,但如果正确处理所有事件,则它应与服务器保持同步。

即使在很大的游戏世界中,这也为我提供了良好的性能。

另一个技巧是,让客户端在不参考服务器的情况下照顾动画,粒子效果等。传递这些信息毫无意义-它们只需要被适当的游戏事件“触发”即可。


6

同步通常分为两部分:增量和绝对。

有时,您必须传输所有内容,因为它很大,但是如果以正确的方式打包,则可以每隔几秒钟执行一次。最好安排一切,以纠正增量刷新的错误。

为了获得实时体验,您必须快速传递一些更改,但只能传递可以更改的属性。例如,如果火箭沿直线飞行,则无需更新位置,每个客户都可以从起点算起。但是当它击中时,您可能会生成有关它的消息,以便每个客户可以在正确的位置爆炸火箭。小故障可以忽略。

当然,只有它们可以影响客户时,您才进行更新!屏幕之外的东西不值得。某些值可以不那么频繁地更新。例如,位置或多或少都非常重要,必须立即发送事件(死亡,射击,爆炸等),而并非直接重要的值可以具有较短的刷新周期,例如记分板,聊天。

数据打包也很重要。您可以在一个UDP包中传输大约1400个字节(取决于配置,这是默认设置),通常会有几个字节的标头。因此,您可以轻松地在一包中更新50-100个单位的位置。


感谢您的建议Matzi。我仍在实现服务器和客户端,但是我将在几天后再检查一次,并且可能会接受您的回答。
哈兹(Haz)2012年

祝你好运!;)
Matzi

1

根据您的游戏,您可能会考虑“同步执行”模型,在该模型中,每个客户端都可以通过简单共享诸如键盘/操纵杆输入和计时器事件之类的不确定输入来玩同一游戏。(与每个客户端运行本地仿真并希望集成远程仿真结果的模型相比)。您的游戏引擎通常需要完全确定性才能正常工作,这可能是沉重的负担,具体取决于游戏。但是,如果游戏已经具有确定性,那么这可能是一种更简单的方法。

#AltDevBlogADay职位涵盖了现代RTS这种方法的某些方面(特别是如何在你的客户开始→运行“不同”的游戏检测)。

不过,请记住要保持简单,除非得到证明。:)


1
这些是使用此方法的Factorio开发人员的精彩读物,暗示了此方法的复杂性,但也表明它是可行的:factorio.com/blog/post/fff-76 factorio.com/blog/post/fff -147 factorio.com/blog/post/fff-188
AaronLS
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.