Monday, August 3, 2020

The Chunking Engine


While testing the map generator with various size worlds, I knew the tilemaps did not need to keep every tile on the map all the time.  Only the visible tiles need to be shown in order to reduce the memory footprint, especially when the maps could be into the millions in tile count.  The arrays that held the map data needed to stay in memory because eventually they will be updated constantly with changes that occur in a "living" world.  Ah, but do they?  When I reach the point in development where I am simulating tree growth, NPC actions, animal migrations and other changes over time, I will have to advance the progression of time-related changes as the chunks are loaded in order to present things as they "are" not as they "were" when the chunk data was last saved to disk.  I look forward to that simulation work.

Getting back to the chunk engine, I needed to develop something that could offload tiles that were not being shown on the screen.  I determined to establish a chunk size setting to allow a player to adjust how small or large the map chunk would be to better align the game with their hardware.  A larger chunk loads tiles less frequently but at the expense of more lag during the load process and more memory consumption.  There are currently three chunk sizes:  20x20, 25x25, and 30x30.  These may be too large, but I will go back and adjust these sizes later when I get to performance testing.

When a map is saved for the first time, each chunk of the map is stored in a single JSON file with the id embedded in the filename.  The player starts in a location on the map. The chunk engine manages the 9 chunks by determining where the player is and checking to make sure that every chunk around the player is loaded.  Taking a 20x20 chunk size into account means that 3600 tiles would be loaded for the top tilemap, and another 3600 would be needed for the core tilemap.  That's a lot of tiles, but Unity seems to be handling it with ease!

9 Chunks Around Player

C0 is the chunk the player is on.  All chunks are loaded in a clockwise order starting with C0 and continuing through the chunks in the order shown.  When a chunk is loaded, the id is stored in a list for reference.  There are two methods that are running simultaneously: a load chunk method and an unload chunk method.  The order of events involved in the load chunk method looks something like this:
  1. Get position of the player on the tilemap.
  2. Check to see if the current chunk id the player is on exists in the list.
  3. If so, skip loading and advance the load direction variable for the next check in the sequence.
  4. If not, load the chunk and store the id in the list.  Other chunk files like flora and game objects are also handled at the same time as the map chunks.
  5. repeat.
That was pretty straightforward.  However, the order of events involved in the unload chunk method are more complicated because there is more to consider and to do to clean up the tilemap.  Loading chunks only loads at most 9 chunks.  But as the player is moving around the map, the potential chunks that must be checked and unloaded increases to 12.  This is because we don't ever want those original 9 chunks around the player to be unloaded.  Therefore, we go to the outer rim of those chunks and check there.  Consider this diagram.



As the player moves around, there is a constant check against the player position and the id of the chunk that is two chunks away from the player's position.  Those need to be saved, then unloaded.  Currently, the check is very precise at exactly 2 chunk sizes away from the player.  This does lead to scenarios where a player could conceivably cause chunks to load and unload in fast repetition.  But I will go back and address this later during performance testing.  I have prepared a silent video which demonstrates the chunk engine at work.  Please note that Pixel Perfect and the camera had not yet been optimized when this video was produced.  The jumpiness seen is mostly due to those factors and not the chunk engine itself.

Chunk Engine Test

Overall, I am very pleased with the performance of the chunk engine.  The loading and unloading happens off screen so the player gets the sense of a seamless world.  This is the goal.  Incidentally, when the player reaches the edge of the map, they currently travel to the edge tiles with the player always in the center of the screen.  In future updates, the cinema camera will allow the player to travel to the edge tiles without the camera revealing the blackness that is beyond the world.

No comments:

Post a Comment

Feel free to leave a comment. All comments are moderated.