周围物体算法

时间:2012-12-16 19:24:33

标签: c++

我正在制作一个游戏,其中(x, y)x y位置ints处可能只存在一个对象。例如,一个对象可能存在于(0, 0)或者它可能不存在,但是不可能同时存在多个对象。

我正在尝试确定哪个STL容器用于解决此问题的最佳方法。

基本上,我从一个对象及其(x, y)位置开始。目标是根据该对象的周围对象确定最高,最大可能的矩形。必须使用当前对象上方和下方的所有对象创建矩形。也就是说,它必须是最高的,它可能基于起始物体位置。

例如,假设以下代表我的对象网格,并且我从位置(3, 4)处的绿色对象开始:

enter image description here

然后,我正在寻找的矩形将由下面的粉红色方块表示:

enter image description here

因此,假设我从(3, 4)处的对象开始,如示例所示,我将需要检查对象是否也存在于(2, 4)(4, 4)(3, 3),和(3, 5)。如果某个对象存在于任何这些位置,我需要重复该对象的过程以找到最大可能的矩形。

这些物品相当罕见,游戏世界庞大。仅仅new整个游戏世界的2D数组似乎不切实际,因为大多数元素都是空的。但是,我需要索引到任何位置以检查对象是否随时存在。

相反,我考虑使用像这样的std::map

std::map< std::pair<int, int>, ObjectData> m_objects;

然后,当我检查周围的对象时,我可以在循环中使用map::find(),检查周围的对象是否存在:

if(m_objects.find(std::pair<3, 4>) != m_objects.end())
{
    //An object exists at (3, 4).
    //Add it to the list of surrounding objects.
}

如果我决定这样做,我可能会对map::find()进行大量调用,但是地图占用的内存比new整个世界的2D数组少得多。< / p>

有没有人对我可以用来找到我要找的东西的简单算法有任何建议?我应该继续使用std::map还是有更好的容器来解决这样的问题?

4 个答案:

答案 0 :(得分:1)

您需要在每个网格位置存储多少数据?如果您只是在寻找一个标志着邻居的旗帜,那么您至少有两个“低技术”解决方案

a)如果您的网格稀疏,每个方块如何保留邻居列表?所以每个方块都知道哪些相邻的方格被占用。当一个正方形被占用或腾空时,你需要做一些工作来维护列表。但邻居列表意味着您根本不需要网格地图

b)如果网格地图位置确实只是点,则每个网格位置使用1位。结果映射将比每个网格点使用字节的结果映射小8x8 = 64倍。位操作正在快速减轻。 10,000x10,000地图将占用100,000,000位或12.5MB(大约)

答案 1 :(得分:0)

如果可能,改进将是使用散列映射。这将允许您至少进行潜在的广泛搜索,预期时间复杂度为O(1)。

这里有一个线程(Mapping two integers to one, in a unique and deterministic way)详细介绍了如何将两个整数散列在一起。

如果您的编译器支持C ++ 11,则可以使用std :: unordered_map。如果没有,boost基本上是相同的:http://www.boost.org/doc/libs/1_38_0/doc/html/boost/unordered_map.html

答案 2 :(得分:0)

您可能需要考虑空间数据结构。如果数据“稀疏”,正如您所说,那么进行四叉树邻域搜索可能会为您节省大量处理能力。我个人会使用一个R树,但这很可能是因为我有一个我编写的R树库,可以轻松导入。

例如,假设您有一个包含10,000个元素的1000x1000网格。假设目前,均匀随机分布,我们(基于密度)预期不会超过,比方说。 。 。在任一维度上接触的三到五个物体的链(在这个密度下,三个垂直取向的物体的链将以0.01%的时间概率发生)。假设所考虑的对象位于(x,y)。从(x-5,y-5)开始并前往(x+5,y+5)的窗口搜索将为您提供最多121个元素的列表,以执行线性搜索。如果你的矩形拣选算法注意到可以形成一个更高的矩形(即如果正在考虑的矩形触及这个11x11边界框的边缘),只需重复窗口搜索另一个5x5} {}区域在原始的一个方向。根据需要重复。

这当然只有在数据非常稀疏的情况下才能正常工作。可能值得调整R树,使得叶子是一个关联。数据结构(即Int -> Int -> Object),但在那时,最好找到适用于更密集数据的解决方案。

我可能过度思考了这一点;在某个地方可能会有一个更简单的解决方案。

关于R树的一些参考文献:

如果我开始对它进行清理,我将使用指向我自己的R-tree实现(公共域)的链接进行编辑。

答案 3 :(得分:0)

这听起来像一个家庭作业问题(因为它有这种奇怪的条件&#34;矩形必须通过使用当前对象上方和下方的所有对象来创建#34;这使得解决方案变得微不足道)。但无论如何我还是试一试。我将使用&#34;像素&#34;而不是&#34;对象&#34;,为方便起见。

如果您的应用程序确实值得重量级解决方案,您可以尝试将像素存储在四叉树中(其叶子包含每个只有几千个像素的普通旧2D阵列)。或者您可以将连续的像素组合成&#34;形状&#34; (例如,你的例子只包含一个&#34;形状&#34;,即使它包含24个单独的像素)。给定一个初始的非结构化像素坐标列表,很容易找到这些形状; google&#34; union-find&#34;。存储连续形状的具体好处是,当您查找最大的矩形时,您只需要考虑与初始像素形状相同的像素。

存储连续形状的一个特定缺点是,如果你的像素对象在移动(例如,如果它们代表roguelike游戏中的怪物),我不确定union-find数据结构是否支持增量更新。您可能必须在每个&#34;框架#34;上运行union-find,这将非常糟糕。

无论如何......让我们说你正在使用std::unordered_map<std::pair<int,int>, ObjectData*>,因为这对我来说听起来很合理。 (你几乎肯定会在地图中存储指针,而不是实际的对象,因为复制所有这些对象比复制指针要快得多。)

typedef std::pair<int, int> Pt;
typedef std::pair<Pt, Pt> Rectangle;
std::unordered_map<Pt, ObjectData *> myObjects;

/* This helper function checks a whole vertical stripe of pixels. */
static bool all_pixels_exist(int x, int min_y, int max_y)
{
    assert(min_y <= max_y);
    for (int y = min_y; y <= max_y; ++y) {
        if (myObjects.find(Pt(x, y)) == myObjects.end())
            return false;
    }
    return true;
}

Rectangle find_tallest_rectangle(int x, int y)
{
    assert(myObjects.find(Pt(x,y)) != myObjects.end());
    int top = y;
    int bottom = y;
    while (myObjects.find(Pt(x, top-1) != myObjects.end()) --top;
    while (myObjects.find(Pt(x, bottom+1) != myObjects.end()) ++bottom;
    // We've now identified the first vertical stripe of pixels.
    // The next step is to "paint-roller" that stripe to the left as far as possible...
    int left = x;
    while (all_pixels_exist(left-1, top, bottom)) --left;
    // ...and to the right.
    int right = x;
    while (all_pixels_exist(right+1, top, bottom)) ++right;
    return Rectangle(Pt(top, left), Pt(bottom, right));
}