存储和读取大型数组

时间:2015-03-31 23:31:57

标签: java arrays

我正在为我的2D游戏重新创建关卡数据结构。以前我已经为这些级别使用了大型2D字节数组,因此我能够毫无问题地将它们保存在内存中,但现在我正在扩展游戏并且无法将所有数据存储在内存中。所以我重新创建了这样的关卡结构。

单个磁贴的代码:

public class Tile {

    public static final int SIZE = 16;

    private short id;
    private short health;
    private boolean solid;

    ...
}

我没有将所有图块存储到一个单独的数组中,而是将大数组拆分成更小的数组 - 块:

public class Chunk {

    public static final int WIDTH = 16;
    public static final int HEIGHT = 16;

    private Tile[][] tiles;

    private int chunkX;
    private int chunkY;

    ... 
}

最后我保留了这些块:

public class Map {

    public static final int EXTRA_DRAW_WIDTH = 0;
    public static final int EXTRA_DRAW_HEIGHT = 0;

    private Chunk[][] chunks;

    private int width;
    private int height;

    ...
}

我现在面临的问题是,我无法弄清楚如何将这些块正确存储到磁盘上,然后在我遍历关卡时逐个读取它们(我只想加载最近的块游戏实体)。到目前为止,我已经尝试过:

  • 将每个块存储在单独的文件中。然而,对于较大的世界,文件数量变得太大,例如4096(我必须保持小尺寸的块以便尽可能地更新游戏实体)。

  • 将所有块存储到单个文本文件中,但我无法找到如何获取所需特定块的快速方法。

  • 我已查看过Fast-serialization,但无法解决如何仅从文件中读取特定块的问题。使用快速序列化和序列化时,我也遇到了一些内存问题。

理想情况下,我希望将所有块放在一个文件中,以便我可以轻松指定要加载的块。是否有任何图书馆或具体方法可以做到这一点?

1 个答案:

答案 0 :(得分:1)

如果您可以确保每个Tile和每个Chunk在磁盘上具有相同的尺寸,则可以将Chunk直接映射到文件中的特定位置。

示例:

SeekableByteChannel channel;
ByteBuffer chunkBuffer;

public void open(Path path) {
    channel = Files.newByteChannel(path, EnumSet.of(READ, WRITE, SPARSE)));
    chunkBuffer = ByteBuffer.allocate(Chunk.SIZE);
}

public void close() {
    channel.close();
    chunkBuffer = null;
}

public void write(Chunk chunk) {
    int index = chunkIndex(chunk.getX(), chunk.getY());
    chunkBuffer.clear();
    chunk.saveInto(chunkBuffer);
    chunkBuffer.flip();
    channel.position(HEADER_SIZE + Chunk.SIZE * index);
    channel.write(chunkBuffer);
}

public Chunk read(int x, int y) {
    int index = chunkIndex(x, y);
    chunkBuffer.clear();
    channel.position(HEADER_SIZE + Chunk.SIZE * index);
    if (channel.read(chunkBuffer) < 0) {
        /* end-of-file or chunk at given index not written yet */
        return null;
    } else {
        chunkBuffer.flip();
        return Chunk.loadFrom(chunkBuffer);
    }
}

/** compute linar index of chunk at position x/y */
private int chunkIndex(int x, int y) {
    return y * MAX_CHUNKS_X + x;
}

保存并加载Chunk个对象:

public class Chunk {
    public static final int WIDTH = 16;
    public static final int HEIGHT = 16;
    public static final int SIZE = WIDTH * HEIGHT * Tile.SIZE;

    private Tile[][] tiles;

    public void saveInto(ByteBuffer buf) {
        for (int x = 0; x < WIDTH; ++x) { 
            for (int y = 0; y < HEIGHT; ++y) {
                tiles[x][y].saveInto(buf);
            }
        }
    }

    public static Chunk loadFrom(ByteBuffer buf) {
        Chunk chunk = new Chunk();
        for (int x = 0; x < WIDTH; ++x) { 
            for (int y = 0; y < HEIGHT; ++y) {
                tiles[x][y] = Tile.loadFrom(buf);
            }
        }
    }
    ...
}

保存并加载Tile个对象:

public class Tile {
    public static final int SIZE = 16;

    private short id;
    private short health;
    private boolean solid;

    public void saveInto(ByteBuffer buf) {
        buf.putShort(id);
        buf.putShort(health);
        buf.put(solid ? 1 : 0);
        ...
        // make sure to always write the same tile size!
        // fill up with placeholder if necessary!
    }

    public static Tile loadFrom(ByteBuffer buf) {
        Tile tile = new Tile();
        tile.id = buf.getShort();
        tile.health = buf.getShort();
        tile.solid = buf.get() == 1;
        ...
    }
}

当然,您可以添加一些范围检查和正确的异常处理!