我会使用类似的东西:
class BlockChunk final
{
public:
static constexpr int sizeXShift = 4, sizeYshift = 8, sizeZshift = 4;
static constexpr int sizeX = 1 << sizeXShift; // 2 ** sizeXShift
static constexpr int sizeY = 1 << sizeYShift;
static constexpr int sizeZ = 1 << sizeZShift;
static constexpr int sizeXRelativeMask = sizeX - 1; // mask to get position mod sizeX (faster than % because negative inputs to % return negative answers which need more adjusting whereas masking always returns the positive answer)
static constexpr int sizeYRelativeMask = sizeY - 1;
static constexpr int sizeZRelativeMask = sizeZ - 1;
static constexpr int sizeXChunkBaseMask = ~sizeXRelativeMask; // mask to get position - relativePosition (aka chunk base position)
static constexpr int sizeYChunkBaseMask = ~sizeYRelativeMask;
static constexpr int sizeZChunkBaseMask = ~sizeZRelativeMask;
private:
Block blocks[sizeX][sizeY][sizeZ];
public:
const PositionI basePosition;
BlockChunk(PositionI basePosition)
: basePosition(basePosition)
{
}
Block &at(PositionI relative)
{
assert(relative.x >= 0 && relative.x < sizeX);
assert(relative.y >= 0 && relative.y < sizeY);
assert(relative.z >= 0 && relative.z < sizeZ); // these asserts are important for finding out-of-bounds bugs
return blocks[relative.x][relative.y][relative.z];
}
static PositionI getRelativePosition(PositionI p)
{
p.x &= sizeXRelativeMask;
p.y &= sizeYRelativeMask;
p.z &= sizeZRelativeMask;
return p;
}
static PositionI getChunkBasePosition(PositionI p)
{
p.x &= sizeXChunkBaseMask;
p.y &= sizeYChunkBaseMask;
p.z &= sizeZChunkBaseMask;
return p;
}
};
class BlockIterator;
class BlockWorldBase
{
friend class BlockIterator;
private:
std::unordered_map<PositionI, std::shared_ptr<BlockChunk>> chunks;
BlockChunk *getOrMakeChunk(PositionI chunkBasePosition)
{
std::shared_ptr<BlockChunk> &chunk = chunks[chunkBasePosition];
if(chunk == nullptr)
chunk = std::make_shared<BlockChunk>(chunkBasePosition);
return chunk.get();
}
};
class BlockWorld;
class BlockIterator final
{
friend class BlockWorld;
private:
BlockChunk *chunk;
BlockWorldBase *world;
PositionI chunkBasePosition, relativePosition;
void updateChunk()
{
chunk = world->getOrMakeChunk(chunkBasePosition);
}
BlockIterator(BlockWorldBase *world, PositionI position)
: chunk(),
world(world),
chunkBasePosition(BlockChunk::getChunkBasePosition(position)),
relativePosition(BlockChunk::getRelativePosition(position))
{
updateChunk();
}
public:
PositionI getPosition() const
{
return relativePosition + chunkBasePosition;
}
Block &get()
{
return chunk->at(relativePosition);
}
BlockIterator &operator +=(PositionI deltaPosition) // move to point to a new block
{
PositionI newRelativePosition = relativePosition + deltaPosition;
if(BlockChunk::getRelativePosition(newRelativePosition) != newRelativePosition) // if the new position is outside of this chunk
{
relativePosition = BlockChunk::getRelativePosition(newRelativePosition);
chunkBasePosition += BlockChunk::getChunkBasePosition(newRelativePosition);
updateChunk();
}
else
{
relativePosition = newRelativePosition;
}
}
friend BlockIterator operator +(PositionI p, BlockIterator bi)
{
bi += p;
return bi;
}
friend BlockIterator operator +(BlockIterator bi, PositionI p)
{
bi += p;
return bi;
}
};
class BlockWorld final : public BlockWorldBase
{
public:
BlockIterator getIterator(PositionI p)
{
return BlockIterator(this, p);
}
};
如果您保留断言并通过 BlockIterator 访问,则永远不应该出现段错误
void drawBlock(Renderer &renderer, BlockIterator bi)
{
BlockIterator nxBlockIterator = bi + PositionI(-1, 0, 0);
BlockIterator pxBlockIterator = bi + PositionI(1, 0, 0);
BlockIterator nyBlockIterator = bi + PositionI(0, -1, 0);
BlockIterator pyBlockIterator = bi + PositionI(0, 1, 0);
BlockIterator nzBlockIterator = bi + PositionI(0, 0, -1);
BlockIterator pzBlockIterator = bi + PositionI(0, 0, 1);
if(nxBlockIterator.get().isPXFaceBlocked())
bi.get().renderNXFace(renderer, bi);
if(pxBlockIterator.get().isNXFaceBlocked())
bi.get().renderPXFace(renderer, bi);
if(nyBlockIterator.get().isPYFaceBlocked())
bi.get().renderNYFace(renderer, bi);
if(pyBlockIterator.get().isNYFaceBlocked())
bi.get().renderPYFace(renderer, bi);
if(nzBlockIterator.get().isPZFaceBlocked())
bi.get().renderNZFace(renderer, bi);
if(pzBlockIterator.get().isNZFaceBlocked())
bi.get().renderPZFace(renderer, bi);
bi.get().renderCenter(renderer, bi);
}