Devlog #15 Taking a closer look at the brain of the AI
Hello everyone! This devlog has 2 parts. The first one is about our general progress since our last devlog, focusing on eye candy, then the second one is a bit more technical and focuses on our modular AI.
Progress so far
The last barrier to testing how all the new things worked was to teach the AI to make sense of it. The AI possible options increased by quite a lot, as now it not only needed to decide what the current character should do but to which character is the best to activate at any given moment. This, in turn, increased its thinking time to around 0.05 seconds in our developer machine. This doesn't sound too bad, but because so far the game only ran on 1 core, it blocked the drawing of a new frame by that amount. So every time the AI turn started, we had a jitter. Example:
This was unacceptable, so it was time for me to separate the AI calculations to a different core, and thanks to that, the jitter is no more.
Another new feature that got added is the standard attack of opportunity system. If a unit leaves a tile that is on an enemy zone of control, then the enemy will get a free basic attack against that unit before it would leave it.
The enemy has learned to use the teleporter as well, enemy reinforcements will arrive by the use of their own teleporter. The player can see where they will arrive 1 round before their arrival.
The new animation system worked perfectly in the testing. In my opinion, it really enhanced the presentation.
Then we did a lot of small improvements as we realized problems while testing. One big thing was that I wanted the player to be able to see at a glance which units have already activated and which ones did not, while not being too obtrusive. We found that the character outlines could relay this information to the player without too much of a problem. So characters that have not activated yet will have their outlines, while those who did do not.
Another thing that I wanted to have a bigger emphasis on is the seeable enemy counter. Especially when it changes! So we moved it to a more prominent location on the UI and gave it an animation when it changes.
The brain of the AI
I talked a lot about modularity and modability of skill in our 13. devlog and these points are super important for our AI as well. I want to give modders the tools to not only create new skills but to be able to teach the AI how to use them as well. To reach these goals, the core of our system is a so-called Utility AI. For those who want a more in-depth explanation of what that is, I can recommend this great GDC video which I think is a great introduction to the topic here!
In very simple layman's terms, it works like this: The AI has possible actions, and every action gets a value between 0-1, which tells the AI how desirable that action is. Then the AI chooses the highest-rated action to execute. In our case, these actions will be the skills(plus their input) the AI can use.
All right, so we can add/remove actions(skills) to the AI so that is great. But how do those actions get evaluated? We are adding a behavior to the action (which is basically a set of considerations) which will calculate it for us. What are these considerations? These are parts of my code that check something in the current game state and return a value between 0-1. For example, there is a health_damage_dealing consideration, which will check how much % of their current health the target would lose by the skill average damage, and it will return that. So if the enemy would lose 100% of their health, this would return 1.
These considerations are the building blocks of the AI. Sadly, modders will not be able to add new considerations to the game but will be able to freely add or remove which considerations are used creating new AI behaviors plus change how to interpret the value coming out of these considerations by redefining the curve which the value goes through before being used.
These behaviors are being defined in JSON files the same way as skills, so they are easily readable and editable. The behavior of the previous example looks like this in practice:
The linear curve defined there basically just there to say to use the consideration value. But if you wanted the AI to target the unit that would have the most remaining health after the attack, you can just reverse the slope of the curve, and that would mean if the consideration return 0, the real value returned by this will be 1.
So that's how our modular AI works in a nutshell. I will of course need to provide documentation of all the possible considerations, curves, and their inputs, but after that, the community will be able to create and modify the game AI to their hearth content. Plus of course, this modularity makes it possible for us to iterate faster on the AI as well.
So that's it for today's devlog! A lot of progress has been made, and I hope we will be able to give you something to not only read but to try out as well in a not-so-distant future! As always, you can show your support to the project in the meantime by wishlisting the game on Steam here.