The grind to end the grind - Implementing the Strategy Pattern


After a bit of procrastinating, I've started making some really good progress on the back-end of the project. A lot of cleaning and polishing has been done, and I've been starting to work towards really improving my code to make the scaling of this project a lot smoother. As I've been learning more, I came across the Strategy Pattern, which has been a huge boon to my current problem: enemy AI.

Unpacking the problem

I really love deep mechanics in games, and I feel that's an element many games barely scratch the surface of, which is understandable sometimes. When this game was finished for the jam, all the AI had three basic behaviours: move towards the nearest grave, move towards the center ritual, and move towards the player. Since they couldn't actually attack, they were mostly just different-looking clones. When I started to really crunch for time, I strove for bringing variety mostly through stats and attempted to make waves based on strategies revolving around those stats - more on that later.

Despite having the ability to work freely on the game now, I have to admit, I was procrastinating on updating the AI. This was my first C#/Unity project implementing any form of AI enemies, so this was all uncharted waters for me. One thing I dreaded was when I was going to have to modify my clunky, single AI script to expand in multiple directions to tailor towards different behavioural needs. I lacked knowledge of how to effectively tackle the issue, so I had to start experimenting and digging around for information.

Modern problems require modern solutions

Recently, I started reading the Head First book on programming design patterns. So far, it's been very effective at communicating the information, especially for someone like me, who struggles with ADHD and reading in general. It's taken a few passes, but I got through the first chapter, explaining the Strategy Pattern. This was a perfectly-timed solution: encapsulating different parts of the AI behaviour, so as to make them modular parts that can be attached, modified, or removed as necessary. With that, I've started to work on implementing this idea.

So far, I've decided to divide the AI into 3 parts: movement, behaviour, and abilities - the last of which is still a paper concept at the moment.

Movement options are still simply moving towards the center ritual, or towards the player, but now they can be modified within their own classes and easily swapped out when the player is nearby. Expansion is relatively painless, and I hope to add more movement options too, such as trying to dodge attacks.

Behaviour is a work in progress, but for sure there will be a passive behaviour that will (generally) be always running. For a starting experiment, I decided to try giving the Druid a speed bonus to nearby enemies. 

Don't @ me about my naming - I'm already paranoid as it is about certain people reading this and ready to beat my ass for it

Since there could potentially be a lot of data to process every frame this is running, I made sure to initialize a hit array and a dictionary for the base enemy components to try and keep everything light, especially if there could be multiple druids at one time.


Thankfully, this wasn't hard at all. It took maybe a few minutes to set up from start to finish - and then a little extra when I realized I should be caching so I'm not abusing heavy methods multiple times, every single frame. With that done, Druids were the first to have their own unique feature - although this will probably be changed in the future. It's still exciting to have taken the first step towards this paradigm shift.

Other changes

In the time since my last post, I've still been keeping busy with other little features to distract myself from the dread of starting all this AI hell. 

  • You can hover the mouse over Skully to show his prompt towards the latest spawn - no more having to guess in case you missed it while finishing off the last wave
  • Implemented object pooling. This wasn't a huge issue for the jam, and frankly might not even be that necessary now - but I find it's good practice and can help keep things tidy. When I had used pooling in the past, I had tried Brackey's solution which seemed unnecessarily clunky. It worked, but it could be better. I came across a video by OneWheelStudio about generics and their usage - which used object pooling as an example implementation. This way felt far more fluid and simpler to use, so I'll definitely be returning to this in the future as well. Highly recommend his channel for other C# videos as well; he deserves far more subscribers!
  • Split up the UI components and decoupled them. When I was scrambling to slap together a basic UI during the jam, everything was just a tightly-connected mess. For a simple game, this isn't that big of a deal, but with how I want to deepen mechanics like the scoring system and how health/mana work, I felt it would be a good idea to keep things separate and clean.
  • Unique waves (new enemy introductions, specific enemy combinations like Gnomes 'n Tomes to emphasize using overloads to kill Druids) will have a quick type-out effect. This is just a little flair that I played with and liked

With most of the core work done, I can really start digging into planning more features and enemy types soon. Some will require more learning: I want to have those little gnomes to be able to actively try and avoid being in direct line of fire from the player, while still heading towards their goal. I have no idea how to approach this, so that's going to be some homework. I look forward to working on this more and feel like I've started to catch a second wind with my creative ideas. Hopefully I'll be able to put out another log sometime later this week with some juicy extra content and potentially a full, playable update!

Leave a comment

Log in with itch.io to leave a comment.