2022/07/25

Montgolfier: Continuing the Journey

It's been a few weeks since I documented an update, and I blame it all on Unity. I mean, I may have some part in it, making assumptions and all, but I'll stick with Unity as the primary culprit. Since the editor so nicely saves scenes for me, I paid no attention to the need for storing game state. To be clear, this is no simple rudimentary game state like player score, but rather the state of the entire world. I presumed it would be available to me during runtime based on the same serialization Unity already handles in the editor. Alas, it is not. So now I've spent nearly 3 weeks refactoring how objects are generated to be able to support a load/save mechanism.

The world is randomly generated based on a template. Sizes are adjusted, resource like trees, loot are spawned, buildings are created, and lots of rules are applied to balance things out. The winds are generated and regenerated over the days. This all works great, and given a "seed" the entire world can be regenerated exactly every time. However, the world then begins to evolve, both on its own (e.g. wind through the passing of the days, items spawning, economy evolving), and through destruction/creation by the player. All of this needs to be saved so the player's journey can be continued.

So after a world was generated, I had more or less two choices: keep only changes made to the environment, or keep the state of the entire universe. The world is not so large that keeping the entire state would be prohibitive, and keeping only changes can eventually be large with the passing of time anyway, so I decided to save the state of the entire game. This was simpler with how I approached the problem, as it blended in well with the generation. If a saved value for some property was available, I would use it, otherwise I would expect a default (most of the time some random value, which would still be generated - keeping the sequencing the same - but discarded if a saved value was available).

Values are saved in few dictionaries by primitive types likes float, ints, vectors etc. Each object whose state needs to be saved implements a "persistence" interface on their script that is called on top-level scene objects during save, and those objects are responsible for calling children's persistence APIs and so on. Because this creates a hierarchy, the keys are stacked so I have cleaner and more identifiable naming scheme rather than each child being responsible for the entire key name (which would not stay very consistent for long). While this was nice from a structure perspective, Unity does not "awake" object in any particular order. This means that the stacked keys (like "islands.1.island.ground.left._position._x") could not be relied on implicitly. Luckily, "awake" is not called on inactive objects, so all I needed to do was to make sure I controlled the activation of objects in the scene. This was the case implicitly when instantiating prefabs, and only a few items were in the scene from the beginning, which I could just set inactive and control during the parents' "awake" call.

So with all that out of the way, and to great relief, saving and loading is fully working, and it's easy enough to expand as I add more objects and functionality. I still wish Unity would just handle this for me, but I'm sure there are reasons why this is not available (maybe it can't be generic enough). And now, back to more interesting adventures.