在内存中实现无尽的映射

时间:2013-03-30 15:43:00

标签: c++ boost maps

我将不得不在程序存储器中实现无限的3D光栅贴图。地图可能也可能不是从[0; 0; 0]开始。地图的Y坐标限制为255,其他可能是无限的。 (是的,现在,你可能已经猜到它是一张Minecraft地图)
我需要创建一些具有简单McMap::getBlock(int x, short y, int z)McMap::setBlock(int x, short y, int z)方法的类。意味着我需要能够读取和写入数据。我还希望能够删除块,以便释放内存
用户应该为此目的做什么?我认为最好的解决方案是一些具有这种结构的表:

int x|short y|int z|int block id|other values...
-----+-------+-----+------------+---------------
   55|     21|  666|           1|

但是如何使用C ++实现这一点,而不使用真正的MySql(这将是真正的矫枉过正)?此外,我不希望在程序退出时保持地图,所以我希望数据程序内存中。 再一次,请考虑地图是无限的,因此坐标可能是 。另外,请不要忘记可以映射非常远的点 另外,需要注意的一件非常重要的事情是:我需要一个有效的方式来获得X,Y和Z坐标的阻挡 - 我想要遍历所有阻止找到其中一个。
我已经包含了库。

2 个答案:

答案 0 :(得分:3)

你可以分块地分解你的地图,比如我的世界。每个块都是 W * H * L (x y z)块。所以,一个块只是一个3d数组。最好的做法是将其包装成1d数组:

BlockType* chunk = new BlockType[W * H * L];
BlockType block = chunk[x + z * W + y * W * H];

这是一个良好的内存管理(并且比将整个可能的映射存储在数组中要好得多)。注意,比访问块中的块是O(1),这里应该非常快。

然后,您可以存储您的块。每个块都有2d坐标,这是它的id。 最快(访问)应该是std :: map:

std::map<ChunkCoord, ChunkType*> map;

快速访问阻止。你需要得到一个块(一个分区应该给你点坐标的大块坐标),然后你得到块。访问块是在O(log(numChunks))。

创建一个块是分配内存并在地图中创建一个新项目。您仍然会受到计算机内存量的限制(无限远不是这个世界的一部分...),这就是为什么像类似Minecraft的游戏经常将未使用的块保存到磁盘上的原因。保存到磁盘是获得近乎无穷无尽的地图的唯一方法。

棘手的一点是找到 W H L 的良好值。为此,我担心你将不得不测试和测量很多......

注意:扩展这个想法导致四叉树。你可以使用它们,但它们可能有太多的内存开销。

答案 1 :(得分:3)

我假设您可能不需要在内存时间拥有Minecraft世界的整个可能区域,因为这将是非常巨大的(1024000000 KM ^ 2)。如果你只想保留任何人在游戏内存时通常会访问的区域,我认为使用STL(标准模板库)访问它是完全可行的。

Minecraft世界总是以16X16X255块的形式加载到游戏中。您可以在std::map中将块存储在程序中。这种方法有一些优点。第一个是它允许基于Far Lands的wiki条目表示远远超出地图可播放区域的位置。它还允许对我的地图进行稀疏表示,这将非常类似于实际的Minecraft地图的渲染方式。只有您用于程序的块才会加载到std::map中,并希望保持内存使用的合理性。您可以代表任何区域,无论其位于可能的Minecraft地图区域的可玩区域内。

要实现这一点,您只需要首先创建world数据类型:

using namespace std;
struct Block
{
     // Whatever information you care to store here...
};
typedef vector<block> Chunk;
typedef map<int, map<int, Chunk> > World;

然后访问单个块:

Block McMap::getBlock(int x, short y, int z)
{
    const int WIDTH = 16; // You might want to store these constants elsewhere
    const int HEIGHT = 255;
    int chunkx = x / WIDTH;
    int chunkz = z / WIDTH;
    return yourWorld[chunkx][chunkz][x + z * WIDTH + y * HEIGHT * WIDTH];
}

删除一个块:

void McMap::eraseChunk(int x, int z)
{
    if (yourWorld.find(x)) // Tests to make sure that row exists in the map.
        yourWorld[x].erase(z);
}

使用此方法的另一个好处是,通过为块创建一个聪明的构造函数而不是像我一样使用typdedef,您可以在需要访问世界中的新块时自动生成块{ {1}}类似于只有在你访问它们时在Minecraft中生成块的方式。每当您访问地图中尚不存在的对象时,它将调用该对象的默认构造函数。