【问题标题】:Storing and reading large arrays存储和读取大型数组
【发布时间】:2015-03-31 23:31:56
【问题描述】:

我正在为我的 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,但也无法弄清楚如何从文件中只读取特定的块。我在使用快速序列化和序列化时也遇到了一些内存问题。

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

【问题讨论】:

    标签: java arrays


    【解决方案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;
            ...
        }
    }
    

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

    【讨论】:

    • HEADER_SIZE 变量是否会在我为地图添加一些额外数据之前添加?如果我忽略该变量,一切似乎都可以正常工作,除了文件很大,只保存一个块会生成一个 1.25KB 的文件。也许我可以通过某种方式对其进行压缩?
    • 我已经解决了大小问题,Tile.SIZE = 16 用于绘图,我为 Tile 类中的每个字节创建了一个不同的变量 DATA = 5。虽然对于更多的块,大小仍然很大,所以我仍在寻找压缩方法。
    • 我认为你应该在你的游戏文件中添加一个包含一些元信息的标题,例如版本号、总大小(以防万一,大小可能会有所不同)、总播放时间、当前玩家位置/标题等。
    • 您期望有多少块?或者:你的游戏世界有多大?
    • 是的,但是ByteBuffer 不会增长,而且因为您只有一个小ByteBuffer 对应一个Chunk,所以这不是问题。不用太担心内存消耗,只要你没有得到OutOfMemoryError。 JVM 将消耗尽可能多的内存。
    猜你喜欢
    • 2012-08-14
    • 2018-08-12
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多