Development: Turn-Based Combat Part 2
Once I got the Entity and Combat states functional, I knew the next thing I wanted to address early in the project was a clear groundwork for both player and enemy actions. I knew I needed to make designing actions and enemies as simple and efficient as possible to ensure scalability. Will this game have 20 types of enemies or hundreds? The attention to the game’s architecture now will make this an easy question to answer as development moves forward.
In this update I no longer have enemy behavior hard coded. They are each assigned a pool of actions they can choose from with weighted probabilities. The config for these pools are handled through Scriptable Objects so that the static data for enemy behavior is not tied to a specific enemy script. Instead any number of enemies can share the Scriptable Object data to allow variety with less code. I’m now able to rapidly prototype different actions and enemy behaviors.
Actions can quickly be made or changed to have different behaviors without breaking any systems. In the above example we can see attacks that hit one target, all targets, and random targets multiple times.
Enemies can be designed to have different personalities by assigning pools of actions with weighted probabilities. The same type of enemy can be more aggressive or passive without the requirement of a new script.
Reduce redundancy and increase independence:
Since starting my journey as a game designer over the last few years it’s become abundantly clear how important applying Design Patterns to my programming can ensure an efficient development process.
A major goal I had for handling player/enemy actions was to make it as simple as possible. I didn’t want a scenario where I needed to recreate the same code for players and enemies to do the same thing. If the code for a basic attack isn’t different between an enemy and player, then the action should be independent of whether a player or enemy is using it. Additionally, I wanted to be able to design and insert new actions into the game freely at no risk of breaking any current systems.
My solution to this was to create a parent class for actions that all actions would inherit from. Any child script of ActionToUse will be able to invoke the functions that use the action and can override the function. I decided to use inheritance over using an Interface, because most of the actions will fundamentally share functions that they all need to invoke, but I still have the flexibility to override the functions in each script. This allows for the variety I’m seeking without the need to individually write each script from top to bottom. I can inherit from the ActionToUse class, override with the logic I want and ship it! Within seconds I can create a new action or change how an action behaves without breaking anything else in the combat system. It’s a serious timesaver!
Another major bonus of this solution is that each action is independent of whether a player or enemy is calling it. It doesn’t need to know! The function’s parameters needs Entity Stats info and all Players/Enemies inherit from the Entity class. This keeps action logic decoupled from what type of Entity is calling it and allows rapid prototyping of actions without breaking any systems.
This also opens up doors for exciting game design opportunities. If I wanted to create an action that allows a player to copy an enemy’s action they just used - it’s not a problem! The action doesn’t care who’s using and can be implemented right now if I wanted. * chef’s kiss *