There are a ton of to be taken into account when picking level formats for a game. For example, designers need to consider what information is needed, how it is accessed, and how much space budget there is for it.
A platformer might need just "collision" and "deadly" flags. A more complex metroidvania would need many more, from different kinds of doors to portals and speed-boosting areas.
Beat'em'ups can usually manage with just a collision flag, or with that and some small additions. Coffee Crisis only had a collision flag; Apeel's Court, our upcoming rogue brawler, will have collision and "jumpable" flags, for obstacles you can't walk through but can jump over.
If the levels are decently sized, you can usually get away with a straight array, containing the rendering data and the needed flags per 8x8 tile. For huge levels, you will need other approaches so you don't waste the 4mb budget on just level maps; metatiles are common.
Genesis rendering data takes two bytes per tile - it's conveniently stored as an array of u16, unsigned 16-bit ints. By default, it uses every bit, but with a clever design, we can pack a couple of bits into it - say, a collision bit maybe.
In Apeel's, we know two things that help us with the packing:
- One: There are two palettes dedicated to levels, of the four.
- Two: A level won't use above 1024 tiles.
As the Genesis tilemap format has two bits for palettes (four values) and we're only using two, fitting into one bit, that makes one bit free for a collision. The format has 11 bits for the tile number, enough for 2048 tiles; as our level budget guarantees we stay below 1024, we can steal another bit from there. Now we have the two bits necessary for the game's collision and jumping metadata, without wasting space with extra arrays.
If your level budget stayed under 512 tiles, you could have another bit from there, and so on.
How do I get them there?
Another age-old question in game dev is getting data to the level format. The usual approaches are:
- Create an editor
- Type things in a text editor
- Hijack an existing editor
The best way depends on the game naturally. A small, single-screen game is usually fastest made with your text editor. Imagine a Pacman level as ASCII art.
For lots of custom functionality or large levels, a custom editor is often needed. It can show collision as an intuitive overlay, place enemy spawn points, or do other things as needed.
And what of in-betweens? Too large to type in, but too small to take the time to make an editor. Adapting an existing editor may be the best choice. Tiled and graphics programs like Gimp and Photoshop may be used for this.
For Coffee Crisis, we made a custom editor. Having the collision as an overlay was quite helpful in making accurate collisions for the levels, at 8x8 tile precision.
For Apeel's, we hijacked Gimp. The collision layers are simple black-and-white, with the grid set to 8 pixels. We then used custom tools to process the exported PNG into an array of ones and zeroes, and another tool inserted those into the tile array inappropriate bit positions.
For both of these beat 'em ups, we were lucky to get by with just the main tilemap array and to have levels small enough not to warrant complex schemes. Their runtime efficiency is practically perfect: reading the rendering information or the collision bit can be done with one read and one AND masking operation. No decompression or metatile indirection overhead.
Space-wise, an array of plain 8x8 tiles is naturally a bit wasteful, but as it's easy to handle and fast at runtime, and if there's the budget for it, there is no reason to invent something more advanced. Even simple compression-like RLE would drop the space quite a bit, but on the other hand, it would increase runtime overhead. It's always a balancing act.