2015-03-31 57 views
1

我正在为我的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; 

    ... 
} 

最后其中I保持大块:

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; 

    ... 
} 

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

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

  • 将所有块存储到一个单独的文本文件,但我无法弄清楚如何获得我需要的特定块的快速方法。

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

理想情况下,我想将所有块放在单个文件中,以便我可以轻松指定要加载哪些块。有没有任何图书馆或特定的方法来做到这一点?

回答

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; 
     ... 
    } 
} 

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

+0

HEADER_SIZE变量是否在一切之前为地图添加额外数据?如果我忽略这个变量,一切似乎都可以正常工作,除非文件很大,只保存一个块就会生成一个1.25KB的文件。也许有一些方法可以将压缩应用于此? – Edd 2015-04-01 09:16:53

+0

我已经想出了尺寸问题,Tile.SIZE = 16是用于绘制的,我为Tile类中的每个字节创建了一个不同的变量DATA = 5。虽然对于更多的块大小仍然相当大,所以我仍然在寻找压缩方法。 – Edd 2015-04-01 10:28:23

+0

我认为你应该为你的游戏文件添加一个标题,它包含一些元信息,例如版本号,总尺寸(如果尺寸可能不同),总播放时间,当前播放器位置/标题等。 – isnot2bad 2015-04-01 16:50:54