使用大量的多维数据集。改善效能?


12

编辑:总而言之,我有一个基于体素的世界(《我的世界》风格(感谢共产主义鸭子)),其性能不佳。我对消息来源并不满意,但希望获得关于如何摆脱它的任何建议。

我正在一个项目中,世界由大量的多维数据集组成(我会给你一个数字,但这是用户定义的世界)。我的测试之一是(48 x 32 x 48)块。

基本上,这些块本身不会做任何事情。他们只是坐在那里。

当涉及到玩家互动时,便开始使用它们。

我需要检查用户鼠标与哪些多维数据集交互(鼠标悬停,单击等),以及在玩家移动时进行碰撞检测。

现在我一开始有大量的滞后,遍历每个块。

我设法通过遍历所有块并找到哪些块在字符的特定范围内,然后仅遍历那些块以进行碰撞检测,来减少这种滞后。

但是,我仍然要压低2fps。

有人对我如何减少这种滞后有其他想法吗?

顺便说一句,我正在使用XNA(C#),是的,它是3d。


你是说我的世界吗?也就是说,体素?
共产党鸭子

4
你有没有看过八叉树?en.wikipedia.org/wiki/Octree
bummzack,2011年

1
您是否尝试过对游戏进行性能分析?它可能显示出花费最多时间的一些关键区域。可能不是您想的那样。
deceleratedcaviar

2
除了只能绘制每个立方体的所有6个面之外,您只能绘制不与任何物体接触的面
David Ashmore

1
@David:是的,否则他可以先停止对每个多维数据集执行一次绘制调用,然后再担心单个多边形。
Olhovsky

Answers:


20

听起来您想学习树木!

我是认真的,如果您当前正在遍历所有多维数据集的数组,那么您确实应该研究各种空间数据结构。对于这种情况,重新想象多维数据集世界的最佳方法是像树一样。

在我们探讨原因之前,先考虑一下我们的问题。我们正在寻找一种解决方案,以尽可能低的成本获取玩家可能与之碰撞的附近立方体的列表。此列表应尽可能小,但要尽可能精确。

现在确定该区域,我们需要将玩家的坐标空间映射到立方体贴图的坐标空间。也就是说,我们需要将播放器的浮点位置映射到多维数据集多维数组的离散索引(示例符号可能是world[31][31][31],即64 * 64 * 64多维数组的正中间)。

我们可以使用相同的离散索引简单地计算周围的块,也许只对附近的多维数据集进行采样,但这仍然需要不断的重新计算,并且不允许放置不离散的任何对象(即,可能无法映射到多维数据集地图)。

理想的情况是一组铲斗,其中包含我们的多维数据集地图特定部分的多维数据集集,并且均等划分,因此我们无需重新计算周围区域,而只需移入或移出这些区域即可。对于任何非平凡的计算,像这样保存我们的数据可以消除所有立方体的迭代,仅消除附近的这些单个集合。

问题是:我们如何实现这一目标?

对于64 * 64 * 64的世界,想象一下它分解为8 * 8 * 8的区域。这意味着在您的世界中,每个轴(X,Y,Z)将有8个区域。这些区域中的每个区域都包含8个多维数据集,可通过此新的简化索引轻松检索它们。

如果您需要对一组附近的多维数据集执行操作,而不是迭代世界中的每个多维数据集,则可以简单地遍历这些区域,将最大迭代次数从原始的64 * 64 * 64(262144)分解为只需520(8 * 8 * 8 + 8)。

现在,从这个区域世界中缩小,并将这些区域放到更大的超级区域中;其中每个超级区域包含2 * 2 * 2个常规区域。由于您的世界当前包含512(8 * 8 * 8)个区域,因此我们可以将8 * 8 * 8 区域划分为64(4 * 4 * 4)个超级区域,方法是将8 区域除以每个超级区域 2 区域。从上面应用相同的逻辑,这将使最大迭代次数从512减少到8,从而找到超区;然后最多64个来查找前进区域(最多72个)!您可以看到这已经为您节省了很多迭代(262144:72)。

我确定您现在可以看到树木的实用性。每个区域都是树上的一个分支,每个超级区域都作为前一个分支。您只需遍历树即可找到所需的内容。使用较小的数据集以最小化总体成本。

下图应帮助您形象化该概念。(图片来自Wikipedia:Octrees): 八位

免责声明:

在如上所述的理想设置中,您的体素世界已经以固定大小的多维数组进行了布局,您可以简单地查询玩家位置,然后用O(1)成本索引周围的方块!(请参阅Olhovsky的说明),但是当您开始考虑自己的世界在体素游戏中很少固定大小时,这将变得更加困难。并且您可能需要数据结构能够将整个超级区域从HDD 加载到内存。与固定大小的多维数组不同,树很容易做到这一点,而不会花费太多时间在组合算法上。


我会说我明白了,但不幸的是我没有。积木的碰撞盒不动,只有玩家的碰撞盒动。而且我现在有一个方法(不循环遍历所有块)将返回播放器5块半径以内的所有块。很抱歉造成麻烦,但您需要澄清吗?顺便说一下,为了简单起见,您可以假设世界为64 x 64 x 64吗?
乔尔

而且我仍然可以达到5fps的速度:(
Joel

我重新输入了答案,让我知道是否有帮助。
deceleratedcaviar

因此,我使用此八叉树技术缩小了玩家可能会遇到的障碍。我很确定我明白了。但是,您是否建议我将碰撞检测范围缩小到仅选择一小部分块后就使用碰撞检测,还是您有其他方法?
乔尔

2
除非播放器相对于立方体的大小很大,否则检查播放器周围的碰撞可能是最快的方法。例如,如果玩家占用的空间不超过一个立方体,那么他最多只需要检查27个周围的立方体即可找到碰撞。这不需要一棵树,因为您可以直接将它们索引到那些多维数据集位置,假设他将多维数据集存储在可以索引到的数组中,并且为每个可能的多维数据集位置分配了一个插槽。
Olhovsky

13

我同意Daniels的回答,因为最可能的原因是遍历大量的盒子,并且通过使用空间分区可以加快游戏的速度-但问题可能还存在于其他地方,您可能会浪费时间。

为了显着提高游戏速度,您需要分析代码。确定瓶颈在哪里,这将使您获得最大的改进。

有很多方法可以分析代码,可以滚动自己的性能分析类(可以使用Stopwatch类(MSDN)),也可以使用PIX来大致了解CPU / GPU的繁忙程度。

您还可以在代码中放置PIX事件标记,这些标记将在PIX的读数中显示为彩色区域。这些功能没有官方的C#接口,但是此线程说明了如何自己创建C#接口。


2
+1,完全同意。您不应该研究算法,而应该分析代码。我的猜测是,您正在遍历各个块(并不多),这与您在每个块中所做的事情无关。最终,是的,您需要一种比暴力破解更好的方法,但是,如果您无法在48x32x48多维数据集上处理简单的迭代,则需要重新考虑每个多维数据集的工作方式,而不是循环方式。
蒂姆·霍尔特

4
@Tim:除非他的玩家足够大以占据48x32x48的空间,否则他不应该在那么多立方体附近的任何地方进行迭代。如果他每帧要遍历73000个多维数据集,那么我可以不做任何配置就告诉您,如果没有别的原因,除了学习如何避免比迭代多进行数万次的迭代,对他来说值得修复它。是必要的。这不是我所说的微观或过早的优化。
Olhovsky

我的播放器小于1个立方体的大小,尽管在某个阶段可能更大(但不大)
Joel

Randomman159:然后,您只需对他周围的27个立方体进行测试即可发现碰撞。看我的答案。
Olhovsky 2011年

6

如果您的播放器相对于多维数据集的大小来说很大,那么您可能想要八叉树或其他空间分区结构,就像其他人建议的那样。

但是,如果您的播放器相对于立方体的尺寸较小,那么检测与立方体碰撞的最快方法可能是对播放器周围的区域进行简单的线性搜索。

由于您的播放器小于1个立方体,因此最多只需要测试与相邻27个立方体的碰撞。

假定您将多维数据集存储在可以索引到的数组中,并且每个多维数据集在数组中都有一个插槽。

正如其他人指出的那样,您需要分析代码以查看实际上使您减速的原因。

但是,如果我不得不猜测的话,我会说您可能正在对每个多维数据集进行抽奖,这到目前为止是您的瓶颈。要解决此问题,您应该研究几何实例化。


或者,您可能有一个包围玩家的“边界框”,然后您只需检查它与哪些对象碰撞即可确定玩家应该与哪些对象碰撞。一个好的物理引擎将能够为您进行所有优化。它也将不仅允许碰撞“障碍”。
deceleratedcaviar

就我个人而言,当我只需要编写两打左右的代码行来为我有效地测试碰撞时,我就不会依靠物理引擎的广泛阶段为我测试73000个多维数据集。另外,他此时可能还没有物理引擎可供使用。
Olhovsky 2011年

1

还有一个建议来加快速度:您的障碍物大约是固定的-这意味着玩家无法与大多数障碍物发生碰撞。向块中添加一个布尔值,指示它们是否公开。(可以通过查看其邻居来重新计算。)无需检查未暴露的块是否存在冲突。

很明显,《我的世界》做了类似的事情-我撞到一个空载的块,这让我对世界有了看法-我可以从坚实的地面上看到,所有显示的都是开放空间(它们是暴露的表面,因此被渲染。)


-1

我的体素引擎遇到了这个问题。

解决方案:(比八叉树简单得多),而不是遍历所有块,只需使用一个方程式即可确定块在块数组中的位置。

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

然后,如果您想查看某个块是否存在:

Blocks[BlockIndex].Type > -1;

或者,您确定该块是否存在。


这里的问题更加复杂,因为要使用3D世界进行测试,鼠标只有2个坐标。如果视图是自上而下的,则可以从3个右索引中找到2个用于鼠标位置,然后从顶部开始-靠近摄像头,循环查找高度,并找到第一个块。
Markus von Broady,2012年
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.