Most of the past 4 weeks was working on ‘providing for the family’ on my main job. But I managed to pull off some interesting pieces of code.
I also got some fine assets models for Find the Gnome… but that’s for another blog.
Video
A short video demonstrating the new AI behavior. See this lovely gnome walk around and (try) to hide behind a tree stump or hide in the vase. And notice the sparkles FX for a bit of vibe.
Hidingspot upgrades
As you can see in the video that accompanies this blog post, I have added a gameplay element that makes gnomes go running around.
At first I was was technically deceived by thinking ‘this is an easy system to make’. I started with a normal ‘rotate around point’. That part was easy. But the hard part was to get multiple gnomes to walk, with their walk speed constant independent of the circle size, a minimum distance to each other, entering the circle on a random spot but at the right distance to each other… and all this while the gnome can still be caught while walking.
In the resulting code, I have a script called ‘RunInCircles’ that manages the registering/deregistering. There is also another script, called ‘RunInCirclesState’, that works together with the previous script. This second script manages the events and keeps track of the state (how many gnomes are there, is it ‘full’ etc). The storage of the gnome data itself is, again, in another script called ‘HidingspotSet’. This thirds scripts handles 2 important things: it makes it so we can start with a set of gnomes in it or it can serialize the state when we want to make a save game.
But from a gameplay mechanics point of view: this circle still needs a bit of tuning. It is part of an ‘overflow system’ that I designed. Because there are situations when all ‘hiding spots’ are taken or visible, and the the gnomes always need something to run to, so these circles are a nice ‘overflow’. However this overflow isnt working that great, currently the gameplay is using it too much. And the gnome itself is too long of a time in this circle (and that makes the gameplay too easy).
I also changed the ‘normal’ hidingspot to function more like the obfuscation spots and the circles scripts. So these normal hidingspots now too can be ‘full’ (it now also uses persistent state and events).
I use the same patterns on all my ‘points’ the gnomes can run to and hide in. But I try not to add too much coupling between different mechanics. I solve this by making most scripts stand-alone without base classes or libraries for re-use of code.
This makes that my gnome has an AI script that needs to behave differently and call different scripts when going to different places. For that I use a simple state machine that switches out logic depending on ‘the type of hiding point’ where its heading to.
New gnome behavior
I already hinted at reworking the gnome AI behavior with the hidingspot upgrades.
Previously I had 1 gnome model for each behavior. One did hide in hidingspots, one other did hide behind obstacles. And I was planning for more different gnomes, with each a unique model to make the player recognize them.
And I had 2 types of hidingspots where gnome could hide in. One of it you could open always, but the other one required the right timing on when a certain animation played.
From a code perspective this meant setting up behavior and animating in a generic way. For both the gnome and its corresponding hidingplace types it would use. That worked well.
However, from a gameplay perspective it wasn’t as interesting as I imagined it to be. During playtesting, it became clear to me that the hidingplace mechanics are more interesting than the gnomes themselves: Gnomes popping up everywhere and hiding everywhere is much more interesting (instead of using 1 type of hidingspot).
So i changed this. And now there is still variation, but its now only the hidingspots that can differ from interaction types instead of the gnomes having variety.
Added benefit of this consolidation of behavior of gnomes into 1 gnome is that it the AI can be more intelligent. It now tries to find a place to hide in, subscribes it selves to that place. And if on the way something happens to it (got full, locked, player just touched it, or is in plain sight now), it immediately knows this and tries to find a new place.
This makes for emergent gameplay because now if you want to catch a gnome and it tries to enter a hidingspot, you can just put it in plain sight or click it and the gnome has to adjust course. This gives your helicopter (the catching mechanic) more time to move in and catch the gnome.
GIT Branching strategy
That brings me to my latest point: with all these code updates I got annoyed with the amount of builds my pipeline was creating.
I currently have my Azure DevOps (with GIT repo) setup so that the main branch triggers releases. And I push to main branch only. And I manually added each commit to the accompanied issue, to track my changes.
While this is a very lean approach to software development and certainly CI/CD, its a bit too much of it. I just had too many updates cluttering my main branch and triggering releases that weren’t that interesting. And there were just too many commits to properly bound to an issue.
So I added manditory pullrequests. With a separate cheap and fast pullrequest build pipe. And the pullrequest requires an Azure DevOps issue to be assigned to it. And it will automatically squash merge all branch commits into 1 commit, further reducing the clutter.
I use the Azure DevOps issue flow now where you can start a branch from an issue. Makes it all much more convenient.
Leave a Reply