用于在游戏中存储实体的数据结构

时间:2011-08-27 11:39:42

标签: algorithm data-structures 2d complexity-theory

我有一个由块组成的平铺地图,每个块都是一个瓦片矩形。现在我想向它添加实体(每个实体都站在特定的tile上),每个循环所有实体都应该调用它们的update()函数,所以我想问一个建议:我应该使用什么数据结构保存他们的位置?

我还不知道我将拥有什么样的方法,但我可能需要一种方法来获取特定区域(可能是一个点)的所有实体以进行绘制。这是一个批评问题,因为可能有一个巨大的地图,如100x100块,其中每个块是30x20块,因此它将是3000x2000块,并且有很多实体,例如1000块,所以如果我将它保存在列表中,它将会非常慢要搜索实体O(n),如果每个实体都进行搜索,则需要O(n^2)

现在我有几个解决方案,但它们都有问题:

kd-tree (适用于2d) - 由于每个循环所有实体都可以更改其位置,因此更新它们的复杂性与重建每个循环的整个树相同O(nlogn)

每个块将保存属于它的实体 - 到目前为止我的最佳解决方案,易于更新,但复杂性高于kd树。

那么有人对这个问题提出建议吗?

2 个答案:

答案 0 :(得分:2)

将图块(位置)映射到该图块上所有实体的列表的字典。所有实体都应具有位置属性和事件通知何时更改,以便可以在每次移动时更新字典。

(没有实体的瓷砖应该没有列表。当一个权利移动到该位置时应该创建该列表,并且当最后一个实体离开该位置时删除该列表。)

答案 1 :(得分:0)

这可能是一个粗略的建议,我相信它可以改进,但这是一个想法:

首先,存储您的位置,使您可以在给定特定对象的情况下以恒定时间访问它们。例如,如果要直接通过实体访问它们,可以将位置结构存储在列表/向量中,并为每个实体指定其位置的指针/引用。

其次,将实体指针/引用或GUID存储在与实体位置相同的结构中,这样您就可以基于位置对象来标识实体。 (可能有一种更好的方式我现在没想到。)

第三,利用扫描和修剪/排序和扫描的一些原则(在3D游戏中很常见):保留两个排序的位置列表/向量,一个在x方向排序,另一个在y方向排序。一个可以保存实际的位置对象,另一个可以保存指针/引用。这些列表可以利用时间一致性,因此保持它们排序的成本不应该太高,除非有很多快速和混乱的运动。

这种设置的一个优点是很容易弄清楚每个物体相对于彼此的位置。想知道Billy the Elf在10个方格内有多少个物体在任何一个方向?检查比利的位置并在两个列表中向前/向后迭代,直到你到达每个方向超过10个方格的实体。

如果您对此概念感兴趣,请查找排序和扫描(也称为扫描和修剪)。您只使用算法的前半部分,但它几乎用于所有主要3D物理引擎中的广角碰撞检测,因此您知道它必须一般快速。 ;)有很多关于它的信息,所以你可能会发现更复杂的实现想法。 (例如,我不喜欢存储指向排序/引用位置结构的排序列表所涉及的间接;使用实际结构更高效,但是如果你想要的话你需要在两个地方更新位置利用持久数组来利用时间一致性。其他人可能已经想到了一个更聪明的设计,现在正在逃避我。)

编辑:我会评论Erik H的想法,但我的代表不够高。我只想说他的想法听起来非常适合你的游戏,特别是如果你有很多实体紧紧地包装在同一个瓷砖或小区域。如果我是你,我可能会在扫除和修剪之前尝试一下。但是,它应该伴随着一个精心策划的内存管理策略:如果你有一个瓦片位置字典,天真地映射到实体的向量,你将有很多内存被分配和释放时,实体从一个磁贴移动到另一个磁贴。相反,你会想要将他的想法实现为更像字典/链接列表组合的东西:

字典键将是tile位置,字典将返回指向链表节点的单个指针。此节点将是同一磁贴上所有实体的链接列表的一部分。每当实体从一个磁贴移动到另一个磁贴时,它将从其当前链表中删除并添加到新磁贴中。如果一个实体移动到一个空的tile,它将独立于一个链表中,并且它应该被添加到字典中。当最后一个实体从图块移动时,应该从字典中删除该图块的条目。这将允许您在没有连续动态分配/释放的情况下移动实体,因为您只是更新指针(并且字典可能会非常节省内存)。

请注意,您不必在链接列表中存储完整的实体;您可以使用轻量级对象(包含指向实际实体的指针或GUID)轻松创建链接列表。