Someone asked me about storing variable-sized tiles in a file. The problem is that if you are keeping only part of your map in memory, you have to be able to easily figure out where in the file the tiles you want are. Tiles can be a variable size if you have a linked list of objects on them, instead of limiting a tile to have only one object.
Here is (most of) my reply:
This is a good question. I have not written a tile map game before but I have often thought about how I would do it, and I had decided the best thing would be to organize tiles into NxN “blocks” and then keep a linked list per block of all the objects in that area.
Storing this format in a file, however, becomes complicated, because the blocks have variable size -- if you have more objects, the block is larger.
What I came up with was that there would be TWO files with map data. (Plus one file with indexing data, which I’ll describe later.) One file would contain the fixed array and one file would contain the linked lists. To load a block of tiles, I can easily figure out where in the fixed-array file to go, and I can load the block from there. THEN that block would contain the location in the other file, of where the linked list goes. The way the linked list file would be stored would be similar to how memory allocation (malloc library) works in C/C++. You’d have to keep track of which parts of the file have data and which do not, and when you want to save your linked list, you would find a place in the file that has enough space for the list.
I am not sure if that made sense, so here are some more details:
A block would be a structure:
Block: int object_file_pos; // where in the linked-list file to go int num_objects; // how many to read from that file Tile tiles; // all the tiles in this block
The first file (
TILE) would be a file of Blocks.
The second file (
OBJ) would be a file of Objects that go in linked lists.
The third file (
INDEX) would keep track of what’s in the second file. (It’s a bit vector of all empty areas in the
To load a block, you’d first load the appropriate block from the
TILE file. Since all the blocks are the same size, you can figure out where to read.
Now we have a block, which tells us where in the
OBJ file to read. We can read num_objects objects from the
To save a block is easy because we know where it goes in the
TILE file, but we don’t want to save it yet because
num_objects are going to change.
However, saving the objects may be harder. First we “deallocate” the objects from the
OBJ file. Let’s say for example that the bit vector telling us what areas are free looks like this:
1 2 0 0 0 <- position in OBJ file - - - 111010011110000001110 <- 1 means there's something there
object_file_pos is 7 and
num_objects is 4, then to “deallocate” the objects, we set those bits to 0:
1 2 0 0 0 - - - 111010000000000001110 ~~~~
Now we need to “allocate” a space in the
OBJ file for the new objects. First we count how many things are in the linked list and put that into
num_objects. Then we look in the bit vector to find a place where
num_objects objects can fit. For example, let’s say that there are now 6 objects. We have to find a place where there are six 0’s in a row. The first place this happens is at position 5, so let’s “allocate” 6 objects at position 5 by changing those 0’s into 1’s:
1 2 0 5 0 0 - - - - 111011111110000001110 ~~~~~~
Now we can go through the list and save the objects at positions 5, 6, 7, 8, 9, 10 in the
OBJ file. When the player saves or quits the game, you can save the bit vector into the
An alternative would be to store objects anywhere there’s space in the
OBJ file, without putting them all close to each other. Then you’d be able to fill the ‘0’ at position 3, for example. This approach lets you save space (because the file doesn’t become fragmented) but loading tiles will be slower because the objects won’t be together.