Alpha Report: Back to Basics
The key words in last week's report were 'if all goes to plan'.
Alas, I wasn't able to get the game to a good place to release to testers last weekend. A few bugs reared their heads with the introduction of much more content. But that's okay! Deadlines like these are designed to motivate, and by that metric it certainly worked.
With four days to totally commit to the project, I made some incredible progress and also learnt a good number of lessons to take with me moving forward. I figured, since I 'failed' to release the alpha last week, I could go over the mistakes and hurdles I faced and how I've since overcome them.
As this was the first time I actually booted a large amount of content into the game, this came with a few surprises. Some were good – my brother has been writing a large amount of dialogue for the game, and I was finally able to load a portion in. It was such a joy to suddenly be able to talk to the characters and experience new dialogue as I would a player playing the game for the first time. It made the characters feel so much more alive, and it really gave me an insight into how the experience would be, playing the game for real.
On Monday evening, at the end of crunch, I encountered a bug that essentially left me to throw my hands up and call it quits. While some characters operated their dialogue perfectly, others would repeat the same line, over and over, no matter what the player did. What really tripped me up here is that all the characters share the same code, so I couldn't figure what the variable was.
I won't bore you with too much minutia, but I had made some glib decisions in my code early on that tripped me up.
As I was testing the game, dialogue files were much smaller, and might have taken a millisecond less time to load in. With the larger files, that extra millisecond meant everything else was flying out of sync. So, while to the naked eye it was loading instantly, that fraction of a second made all the difference — hence why some characters worked better than others, some had larger dialogue data than others.
All it took was a simple line of code, something I previously neglected, that sits and waits for the data to be processed before confirming and continuing.
I had felt confident about The Garden Path's performance, having done tests where I would load hundreds of trees in and maintain a steady framerate.
While The Garden Path runs a single, large and seamless map, I had developed a simple system where all trees, flowers, plants and so on, are just co-ordinate data on the map until the player gets close enough.
In testing, however, the player does a whole lot of moving, which I rather sillily hadn't accounted for. Once the map was full of plants, the sheer amount of loading in and loading out as the player triggers the areas to load in, and leaves the areas to load out, for hundreds of plants — the game grinded to an absolute crawl.
This was especially demoralizing, as I had pinned so much of the game on being a large, seamless map, but fortunately there was a solution I landed on that worked a charm.
The big 'aha' moment for me was remembering Godot has something called a ResourceInteractiveLoader. It's primarily there to be used for loading screens, to prevent the game from crashing as it's overloaded from loading in a large game.
I use it for my own loading screens, and I was able to lift the code directly and place into each spawn point for every item.
Essentially, instead of the game demanding something be loaded now, it delegates the loading to the background, dripping in the loaded content as the game has resources to do so. As loading in a flower (for instance) is actually an incredibly low-resource task, the reality is that there's no waiting time at all – it just prevents a hundred flowers, say, being loaded all at the same time, all at top priority.
(Writing this I stumbled on a commit that says this feature will be removed shortly in favour of multithreading. So that'll be on the to-do.)
Couple this with a new system that holds onto already loaded assets so the game isn't unnecessarily loading them twice, and a simple timer that times down should a player leave the zone (preventing it from loading in and out if the player backtracks) and The Garden Path feels smoother than ever to play — a real boost.
Lastly, I've slightly adapted the artwork in the game to be both quicker to produce and easier to read on screen. It's only a minor stylistic change from the big update some months back. It'll be a big time saver in the long run, prioritizing larger and more bold brush strokes over the fine detailing I had used before. So much of the detail was getting lost and even confused once assets were shrunk down into world, so it made sense to double down on the details that really matter. Best yet, none of the old artwork goes to waste, as it only takes a minute or two to adjust the shading to fit into the new scheme.
I had a moment of clarity on Monday as I watched the variety of different veg headed villagers wander around the game, as I thought just how close this was to how I visualized the game in my head 3-4 years ago. It occurred to me the only thing that still didn't feel quite right was the player character.
I decided it was time for a complete rebuild of the player node, which was still using the puppet structure I set out in 2018. Adapting the existing artwork, I've settled on a look for the player that's more mature and proportioned.
It allowed me to rebuild the character from the ground up with everything I've learnt over the last years, resulting in a puppet that's really good to work with. This has allowed me to ditch the old running animation (my first ever animation in Godot) and create a new running animation that I'm very proud of, one that actually feels like you're running through the garden, rather than just gliding.
It's hard to describe without playing it yourself, but moving through the world feels so much more satisfying now.
If I can get something as fundamental as that right, then I'm hopeful the rest will follow.