Its funny you posted this question because I implemented this into my game yesterday in about an hour. I used a different method called RLE (Run Length Encoding) to compress the amount of data being saved to the file. What I did was each chunk of the world has its own save file with the name of the file being “chunk_”. It goes through each row of the chunk’s tiles and uses the RLE compression method.
The way that the RLE compression works is it takes a common pattern and compresses it for you. There are many ways to implement and interpret the compression method but I’ll explain to you my implementation. So say that you have 10 blocks with the ID 5 in a straight line in a row. Instead of formatting those 10 blocks as “5 5 5 5 5 5 5 5 5 5”, where each number is the ID of the block, it instead formats as “5-10” where the first number is the ID and the second number is the amount of blocks in a row. So if you have a line that is “5 5 5 3 3 4 1 1 1 1 1 1” it would instead be saved as “5-3 3-2 4-1 1-6”. This works very well for chunks that are large.
So my implementation goes through and does that for each row and then each line in the saved file is a corresponding row in the chunk. Here is my code:
public static void saveChunk(String worldFileName, Chunk chunk) {
//.... I load the file and some other stuff up here. That's also why there is a try/catch
try {
String totalToken = Chunk.CHUNK_SIZE + " " + Chunk.CHUNK_SIZE + "\n"; //First line is the width and the height of the chunk
for (int y = 0; y < Chunk.CHUNK_SIZE; y++) { //Go through each row
BlockType lastType = null;
int sameCount = 0;
for (int x = 0; x < Chunk.CHUNK_SIZE; x++) { //Go through each column
BlockType type = chunk.getBlock(x, y); //Get the block type
if (type == null) //In case it is null which it is in newly generated chunks
type = BlockType.AIR;
if (lastType == null) //if it is the first instance because only the first run will be null
lastType = type;
if (type == lastType) { //We're still counting the same blocktype
sameCount++;
} else if (x != 0){ //We found a new blocktype. Save in format <id>-<count>
totalToken += lastType.getId() + "-" + sameCount + " "; //Add it to the row
sameCount = 1; //Restart the count
lastType = type; //Set the last type to our new type
}
}
if (sameCount != 0) { //If there are values not saved and still counting at the end of the row before going to the next...
totalToken += lastType.getId() + "-" + sameCount + "\n"; //Add it to the row
} else {
totalToken += "\n"; //Add the new line and continue to the next row
}
}
handle.writeString(totalToken, false); //Write to file and dont append
} catch (IOException e) {
e.printStackTrace();
}
}
When loading it it is just the opposite
public static Chunk loadChunk(FileHandle handle, int x, int y) {
if (handle.exists()) {
Chunk chunk = new Chunk(x, y); //Create a new chunk
String[] rows = handle.readString().split("\n"); //Get all rows
String[] dimensions = rows[0].split(" "); //Get the dimensions from the first line
int width = Integer.parseInt(dimensions[0]); //get the width
int height = Integer.parseInt(dimensions[1]); //get the height
for (int i = 0; i < height; i++) { //Go through each row
String[] rowData = rows[i + 1].split(" "); //Get each column information. + 1 so that we skip the dimension row
int xOffset = 0; //The offset from the last data count. Keeps track of what column (x) to set the block to
for (int j = 0; j < rowData.length; j++) { //Go through each column entry
String[] data = rowData[j].split("-"); //Split the id and count
int id = Integer.parseInt(data[0]); //Get the ID
int count = Integer.parseInt(data[1]); //Get the count
for (int k = 0; k < count; k++) {
chunk.setBlock(BlockType.values()[id], xOffset + k, i); //Set the block
}
xOffset += count; //Add the column count
}
}
chunk.setGenerated(true);
return chunk;
}
return null; //Chunk file doesn't exist
}
Hopefully this helps out!