A between-months blog post here to show the cool stuff I've been working on regarding the AI in BLISTER. Like most of the game we're reworking the AI. This time around the enemies you'll be fighting against are going to be much smarter and more aware of their surroundings and situation, planning accordingly.
I've based the design of the Insurgent AI on the AI system in F.E.A.R. It has some of the best and most fun enemy AI I and everyone else have ever played against, so it's makes sense to use it as the base of BLISTER's enemy AI. The guys at Monolith published a paper on how their AI works and after many reads and more research I started work on it last week. You can read their paper here. Give it a read cause it's pure brilliance.
At a high level it works like this:
Each AI keeps a struct called "AIState" which holds variables that are updated each tick of the AI. Examples of these variables are "HasAmmo", "HasValidCover", "IsInMeleeRange" or "IsEnemyDead".
Goals and Tasks
AI have a set of possible Goals and Tasks (In fear they called them actions)
Goals could be things like "KillEnemy" "FindCover" or "HelpTeammate". Each of these goals have a goal "state" that uses the same struct as the previously mentioned AIState, except in this case its the state that must be reached for the goal to be satisfied. The "KillEnemy" goal has the goal state of "IsEnemyDead" as true, for example.
Goals each have a priority set by the programmer, so the AI Planner knows which goal is the most important to satisfy first.
Tasks also use the AIState struct, but in two ways. They have a precondition and an effect. The precondition is the state that is required to be true in order for the task to be usable. The attack task requires that "HasAmmo" is true. The effect state struct denotes the effect that successful completion of this task has upon the AIState, in the case of the attack task, it has the effect of "IsEnemyDead" being set to true.
Tasks have a cost set by the programmer so that the Planner can figure out the best way to satisfy the goal it has based on the cost of possible actions it can take.
Tasks contain in them the code to make the AI do things, like fire their weapon or take cover, and are run when they are selected by the AI as the task they need to carry out at any moment.
But how are they selected?
Goal and Task Selection (Planning)
So it's all well and good to have goals and tasks, but there needs to be a system to "link them together". Let's call this the Planner. Each AI tick (which is between 0.4 and 0.6 seconds right now) the AI runs code which updates the current AIState by performing a number of checks, and based on those checks adds appropriate goals to the AI's array of goals. For example, if the AI can see the player, it will add the "KillEnemy" goal to the array of goals.
The Planner then picks a task to satisfy this goal. It checks to see if the effect of a task equals the requirements of its current goal, and then checks the cost of this task. The Planner will always prefer the task with a lower cost, however sometimes might not be able to because of a task's precondition.
For example, "attack" and "attackMelee" both have the same effect, however "attack" has the precondition that the AI has ammo, and "attackMelee" has the precondition that the AI is within melee range. "attackMelee" has a lower cost, and so if in melee range the AI will prefer doing a melee attack rather than firing their gun.
If the AI is not in Melee range, and has no ammo then neither "attack" nor "attackMelee" can be used, and so the planner will attempt to find an action that satisfies the preconditions of these tasks. The "reload" task satisfies the preconditions of "attack", and so when the AI runs out of ammo, it will determine that it needs to use the "reload" task to replenish its ammo and then can perform the "attack" task.
All This Combined:
Here is a demonstration video of where the current system is at. There just are 2 goals and 4 tasks in this version. The goals are "KillEnemy" and "FindCover", while the tasks are "attack", "attackMelee", "reload" and "takeCover". This is just scratching the surface of the goals and actions that can be added, and already produces some interesting behaviour, far better than what was in the "old" BLISTER.