一种存储潜在无限2D地图数据的方法?


29

我有一个2D平台程序,当前可以处理100 x 100的块,块坐标存储为long,因此这是地图的唯一限制(maxlong * maxlong)。所有实体位置等都与块相关,因此没有限制。

我遇到的问题是如何在没有成千上万个文件的情况下存储和访问这些块。有什么想法可以实现快速低成本的高清存档格式,而无需一次打开所有内容?


2
您可能会寻求更多启发的一些数据结构是稀疏矩阵(多级)页表
Andrew Russell

低优先级:您能否澄清“长”数据类型是32位还是64位?
Randolf Richardson 2011年

2
@Randolf假定这是C#,想必他表示的long是64位的C#(因此maxlong为Int64.MaxValue)。
Andrew Russell

在我的博客中,Notch对于Minecraft中的无限地图有一些有趣的事情要说:notch.tumblr.com/post/3746989361/terrain-generation-part-1
dlras2 2011年

Answers:


17

为您的游戏创建自定义地图格式。它比您想象的要容易。只需使用BinaryWriter类。首先将标题写成几个int或uint。标头中包含的信息:

  • 文件格式的幻数字符串/幻数。
  • 此文件中描述的块的开始/结束/大小

而且(这是性能关键部分

  • 描述文件内起始位置的整数。因此,您不必搜索特定的块。

使用上述方法,您可以(并且应该)创建文件内容的索引,其中包含某种描述(用户为区域/块指定的名称,或者仅是坐标),并将文件中的位置作为第二个值。

然后,当您要加载特定的块时,只需在索引内部进行搜索。找到位置后,只需设置fileStream.Position = PositionOfChunkFromIndex即可加载它。

这与文件格式的设计有关,其中标题最有效地描述了文件的内容。

只需使用组成的自定义扩展名保存文件即可。

奖励:将BZip2压缩添加到文件的特定区域/整个内容(而不是标头!),因此您可以从文件中解压缩特定的块,从而占用很小的内存。


12
值得指出的是,如果您要即时修改此文件,则需要固定大小或外部的标头/索引,这样就可以向文件中添加块,而不必重写整个文件(由于偏移量的变化)。
安德鲁·罗素

那时,您不只是实现一个平面文件数据库吗?
Ape-in​​ago

13

我遇到了类似的问题,并决定创建自己的结构来处理数据。它松散地基于四叉树,但是在所有方向上都具有无限的(至少与Int一样大)可扩展性。它旨在处理从中心点扩展的基于网格的数据,就像现在的Minecraft一样。它在内存中节省空间,而且速度非常快。

您可以为每个节点指定最小大小(大小为7,即128x128),一旦任何节点填充了指定百分比的子节点,它就会自动将其自身展平为二维数组。这意味着人口非常稠密的部分(例如,完全被勘探的大陆)将具有阵列的性能(非常快),但是人口稀疏的部分(例如,有人在上而下徘徊但未探索内陆的海岸线)将具有性能。具有良好的性能和较低的内存使用率。

我的代码可以在这里找到。该代码是完整的,经过测试的(单元测试和负载测试),并且经过了优化。但是,内部工作方式还没有得到很好的记录,但是所有公共方法都可以使用。如果有人决定尝试,请随时向我提出问题或评论。

我还没有用它来将数据存储到文件中,但这是一个有趣的问题,接下来我可能会解决。


所以这基本上是一棵可扩展的树,对吧?我想念什么?
kaoD 2011年

2
可扩展树的最大改进是将“树”的某些节点(填充程度很高)(默认为70%)“展平”为2D数组,而不是使其结构像树一样。这可以为您提供数组查找的速度,而没有无限数组的(无限)大小。
dlras2 2011年

叶子和内部节点都对吧?有趣的想法,可能会产生良好的效果,如果需要,我会尝试一下。顺便说一句,+ 1给出了代码和快速答案!哦,单元测试也完成了,我(
很难

我们在工作中没有进行任何单元测试,所以很遗憾,这是我的反叛方式。.我确实为此制作了一个演示应用程序,该应用程序显示了它是如何填充和展平的,因此,如果我能在接下来的几场中进行清理天,因此它很适合展示,我也将其发布在这里。当您看到它时,它会变得更加有意义。
dlras2 2011年

1
我看不见它,对不起!我仍然想清理它,但是我正在慢慢地在课堂和家庭作业之间重做一些代码,因此不会有一段时间了。现在,这里是旧的,不太漂亮的演示:j.mp/qIwKYt通过不漂亮,我的部分意思是它需要大量解释,所以请不要忘记阅读自述文件并随时在此处提问或通过电子邮件。
dlras2 2011年

3

您可以改用数据库-PostgreSQL具有一些特殊的索引功能,这些索引功能针对通过X和Y坐标定位的此类数据进行了优化。您还可以指定返回的数据在一定半径内,而不是在正方形或长方形区域内。

  PostgreSQL(免费和开放原始码)
  http://www.postgresql.org/

还有其他数据库,对于客户端,您可能会发现某些类型更适合此类型,因为它们可以独立运行(由您的游戏客户端应用程序启动),也可以作为代码库的一部分包含在内您可以“随便使用”。好处是您不必设计索引方案,因为大多数SQL数据库引擎已经很好地做到了这一点。

数据库方法的一个优势是您可以使块变小(或完全摆脱块并直接使用图块,但是根据设计的不同,至少使用许多图块的小块/组可能更有效),然后使用SQL查询带来比可见区域更大的区域。通过预加载以重叠附近的不可见区域,可以在玩家移动角色之前准备好磁贴,从而带来更好(希望更平滑)的游戏体验。

我注意到有些游戏在第一次获取后在本地硬盘上保留了地图数据的“缓存”(这无疑是减少网络I / O),例如Ashen Empires:

  灰烬帝国(免费游戏,精美的2D实现)
  http://www.ashenempires.com/

跟踪每个块/小块的“最新更新”时间戳也将有所帮助,因为对于可获得本地存储数据的地方,SQL查询可能包括附加的“ WHERE timestamp_column> $ local_timestamp”子句,以便仅获取更新的块/小块下载(这样节省带宽的两个好处是降低了连接成本,并减少了播放器的延迟,当您的游戏流行时,延迟将变得更加明显)。

屏幕截图来自《灰烬帝国》(几个角色在当地一家银行,从地板上那些骨头的外观看来,好像有几个骷髅怪兽一定已经流连了,很可能被当地城镇的守卫杀了):

enter image description here


2

请勿存储和访问它们,仅存储必要的随机种子以及玩家对地图的更改。然后在运行时生成所需的部分(运行您的生成算法,然后应用玩家的更改)。使用正确且一致的生成过程,对于相同的起始种子,生成的图将始终相同。

从理论上讲,您可以执行字面无限的映射,这样可以将其保存到很小的文件中。


@Josh Petrie感谢您对我的帖子所进行的重大而出色的语言/风格更改。可惜我不能同意编辑:-D
sh代码

1

有什么方法可以划分块(您世界中某种“次大陆/国家”)?因此,也许您可​​以使用某种索引文件,以使您快速找到需要加载的子文件/较大文件的一部分才能在内存中存储块...


总有一种方法可以对块进行分区。总是。无论它们是否对玩家可见/是否与系统的其余部分相关,总有一种方法可以将世界数据划分为多个块,通常以多种不同方式进行。
sh代码

0

您可以从Minecraft中获得灵感。最初,他们每个块都有一个文件。现在他们使用MCRegion格式,该格式将块分为32x32区域,并在每个文件中存储其中之一。

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.