GUI: Continue, MapGen and Startup Time
Another busy and productive week, with work on a few different fronts.
GUI: integration with game
The previous title screen that I shared last week was a standalone project with everything else turned off. I thought I should integrate things to the game code as soon as possible, and this caused a few headaches, particularly regarding display. Apparently, there are "official" recommendations for Godot apps regarding what's best for what type of game, pixel art, etc, which can be found here. I went for the non-pixel-art suggestions, because my shaders typically operate at higher resolution, so I thought I might be limiting myself severely. The other topic, which is startup performance, is discussed further down.
GUI: Continue screen
First I tackled the continue screen. Browsing through gameuidatabase, I fixated a bit on this, this and this and used them as rough references. The result is shown in the above video. Simple, effective, shows things I think are interesting. Could be more polished, but this can be left for a later stage. What's important is to figure out how the GUI system works and have something remotely representative.
To be able to have these nice metadata about each save, I had to implement the relevant functionality, but that was not hard, just a bit of extra work at save time: take a screenshot (and resize it to 512x288) and saving an additional json file (for simplicity).
Part of this work was also to figure out how to communicate between the different screens. I decided to have all UI in the main scene, under a UI node, and toggle things on and off. It's possibly not great practice, but I wanted to get moving. There's a helper script that handles the signals to move from one screen to the next and toggle things on and off appropriately. Maybe at some point I'll test if it's better to instantiate different scene files/nodes, but generally, instantiating is always more expensive and garbage-y than turning things on and off.
GUI: Continue screen
Next thing that was tackled was the overworld map generation screen. My idea/approach for this is to have the settings on the right and the generated map on the left. Of course the awesome thing about it is to play around with the settings and get a new world instantly. That's work in progress. Another important aspect here is: which settings should a player control?. If you, dear reader, have an opinion, I'm happy to hear! I've put some placeholder ones for now, like temperature, humidity, landmass style (e.g. continents, islands, etc) and seed, but I can't think of others that could be very important/desirable. I have about 50 developer-friendly parameters, but I don't want to expose those. My goal is to have a set of parameters that are intuitive and always generate good/playable maps.
Another goal for this screen is to show the biome distribution, so that the player sees for example how much tundra, swamp, desert etc is in the world, without having to scan the image.
Part of this work resulted in figuring out the "tiled" mode of nine patch rects, so I used my typical meander pattern.
Startup optimisation
After integrating the main menu and continue screens in the game code I realised that development of new UI would be tedious if done using the game's code. The reason is that the startup performance was not great. Depending on the moon's phase and Godot's and .NET's whims (aka hidden caches), sometimes the game started in the editor within 5 seconds, and sometimes it took 10. Utterly unacceptable. Can't iterate on any work like this. Why so much? Well the profiler gave some info, and I acted on it:
JSON config database. Biggest hit. At every startup of the application, I loaded 1MB of json configuration data, split in 85 files. So much work, so unnecessary. JSON is nice for human readability, but I want binary blazing speed for when I don't need the human readability. Solution? Ensure that the entire config database hierarchy can serialise to binary, and that it's as robust to mistakes as possible. That resulted in tedious annotation for MemoryPack for loads of classes, and the end result is a nice LZ4-comrpessed binary file that loads pretty quickly. So, instead of reading any JSON, I just read that on startup. And what happens if a change is made to a json file? For this, I always check the folder hierarchy where the json data are, and if I detect any file date modified that is newer than my cache file, I rebuild the cache. Simple, and works. This removed 2.9sec from startup time.
Shader compilation. Second biggest hit. Every time I startup the application, I end up compiling several shaders to bytecode. Why not just save the bytecode? So, I implemented a similar caching scheme to the above (also fixing a bad bug in the meantime!) and I removed 2.5sec from startup time.
Fonts. Last week or so I was testing different fonts, and that means loading a lot of them at startup time. Well, huge mistake for performance. So, while I'm not comparing fonts, I just load a single one. Removed 0.7sec from startup time.
Now, I was gloating after all this, thinking "oh my, if these are the editor savings, the exported .exe performance should be great!". Well, turns out that it's exactly the same, and I'm not sure if I should be happy with this or not: I've optimised the editor performance, but there's still pending cost. I still need about 4 seconds for startup.
I started looking at ResourceFormatLoader for loading binaries and other json files more in-line to what Godot works well with, but after a bit of digging I'm not eager to commit to that work yet. If you have any experience on the matter, please let me know!
Misc
I've created a script to help with expected game publish actions, like for example generating the serialisation tree (this can be used for save compatibility checks) or getting the commit hash for the game's title screen.
I've added some button sounds, hovering and clicking, just to see how that works. Sounds are not quite fitting at the moment, but I couldn't find better ones after 30' search.
I also tinkered a bit with the font. I've added a slight grey outline and tried scaling the button text when hovered (for the Begin, Continue, Settings, Quit). I didn't like the scaling version so that got ditched.
Phew, that's this week done! Next few weeks are going to be quite busy IRL so I don't expect to have as many updates. But, as always, things move forward.