I started to add a new enemy this week, vampires. This revealed a problem with my AI framework. Vampires have the same move and attack behavior of a normal enemy, but they have some additional behaviors as well. For instance, they can change into a bat and will use that ability when their health is low to temporarily flee and regenerate health. Prior to vampires, the AI framework worked fine. Each time I needed to give an actor a different behavior, I’d simply add a new AI class. I had a single enemy AI class, a neutral NPC class, and a few classes for actors that do something but aren’t sentient, like fire and gas. This worked because the logic for each class was completely different. The vampire AI revealed a problem because it needed some of the standard enemy behaviors and its own unique behaviors. I spent a couple of days thinking about what to do about this. The solution came to me when I identified the pieces of the AI class that needed to change with each enemy: choosing which actors to track, choosing which observations to react to, and choosing an action from a list of potential actions. I defined interfaces for each of these and created standard enemy and vampire implementations. I extracted shared logic, such as determining all of the potential attacks an enemy has, into new classes so that the logic could be reused. I reduced the enemy AI class to the logic that was applicable to all enemies, which was mainly state management. I can now easily add new enemy behaviors without having to replicate code.
Rework can be a discouraging exercise, especially this far into a project. It doesn’t add anything to the game from a player standpoint. It doesn’t concretely move the project closer to the finish line, but there’s an expectation that it will save time in the long run. It can feed self-doubt (if I had written good code the first time around, I wouldn’t have needed to rework it). There’s a risk of over-engineering or building capabilities that you’ll never need. In this case, I almost scrapped my entire AI framework and considered implementing it using a Unity asset, Opsive Behavior Designer. I actually bought the asset and read the documentation. It seems like a great tool that provides a visual designer for AI behavior trees. It also supports utility AI within a behavior tree, which is essentially what my AI framework is doing in code currently. However, I decided to rework my existing code instead because it took less time to do.
With the AI rework filling up the week, the vampire wasn’t completed. I should be able to easily finish adding it next week. I’ll add one or two more enemies to test out the reworked framework. I will do the same with abilities, which went through a similar process recently of having to be reworked to support new types.