Creating interesting AI
Author:|| ben mercer
Submitted:|| 29th May, 2007
I've learned a lot this year about creating game AI. This is specifically enemy AI for a top down third person shooter, but I think a lot of what I have learned can be transferred to other types of games.
In this article I don't aim to go into the specifics of coding an AI engine for a certain type of game. I'm shooting for a more general overview on what you, the developer should consider if you want to make smart and interesting AI a key part of your gameplay.
==Response to the player==
One of the platform game conventions is that enemies will patrol back and forth and perhaps shoot something at the player. In most platform games the AI has little or no connection to the player whatsoever and is simply a moving hazard presented as a monster. A Platformer released a while back called "a game with a kitty" somewhat broke this tradition; there was a certain type of enemy that intelligently followed the player and attempted to jump on their head, Mario style.
I think of this response to the actions of the player as a defining factor of AI itself. If a monster takes account of the player's actions, it can be considered AI, otherwise it is simply a hazard.
==Situational awareness and response==
Having the AI aware of the player is one thing, but the next step is to have the AI aware of it's situation. The intelligent following enemy in "kitty" would run towards the player, and when it was within a certain distance of the player, attempt to jump on his head. But then what happens when the player jumps onto a ledge?
With no awareness of the situation, the enemy would just have slavishly continued to walk into the wall in an attempt to reach the player. However, in actual fact the enemy could jump up onto a ledge to reach the player.
In order to execute this well, you need to either consider all possibilities or eliminate the ones that could cause unwanted or unnatural behaviour by the AI. Think to yourself, if I was chasing the player, how would I know when to jump up onto a ledge? First of all there must be a wall of some sort in between you and the player, and secondly you need to jump when you are a certain distance from the wall. So a possible method of implementation would be to use an active object as a detector to check when there is a wall nearby that will obstruct the AI's path.
But there are other possibilities. What if the player jumped up onto a floating platform, or a platform on top of an overhang? The AI would either misjudge the jump or fail to jump at all in the case of a floating platform. So you have two choices; either do not allow any overhangs or floating platforms in areas where this type of enemy is present, or find a way to make the AI behave appropriately. My solution would be to place "helper" objects underneath an overhang or floating platform to simply make the AI aware that it is there so that it can react.
"Helper" objects are invaluable in most cases to good situational awareness. Even in basic Platformer AI it is usually preferable to place objects that tell monsters to change direction next to ledges than devise some convoluted method of detecting a ledge. One thing to note is that pretty much anything can be done without using helper objects, but it is usually better for optimisation and less complicated to program if you do use them!
One good example of helper objects aiding situational awareness is in my shooter in progress. If you shoot an enemy, he will immediately make it his priority to get out of your line of fire. Sounds difficult to program, but in reality it is not that complicated. On every corner and obstacle on the map, i have manually placed helper objects named "cover points". There are four different types, North South, East and West. So when an enemy decides to take cover, all he has to do is think "The player is south of my position, so i need to go to the nearest 'North' cover point" or "The player is west of my position, so I need to go to the nearest 'East' cover point".
Your AI will be most intelligent if think carefully about what is an intelligent thing to do in a given situation. If you get shot, would you just stand there and try to shoot back despite the shock of being hit by a bullet? If a grenade landed at your feet would you just wait 3 seconds and hope it was a dud? Nope!
So you've made AI that can react intelligently to the player and the situation. The first time someone plays your game they will be wowed by the perceptiveness and intelligence of the AI... but when they decide to play it again, they will realise that the AI reacts in EXACTLY the same way EVERY time! So the value of your game diminishes rather rapidly.
In my opinion the piece de resistance to of a good AI engine is unpredictability. Unpredictability not only gives the gameplay more lasting value, but also makes the AI better. If the AI reacts in exactly the same way every time, the player will soon find a failsafe routine for dispatching them with ease. Routine leads to monotony, monotony leads to boredom, boredom leads to... suffering! And you wouldn't want that for your player would you?
In my experience a good way to make AI unpredictable is to randomise certain parameters of each individual's behaviour. For example, in my shooter game there is a behaviour in which a group of enemies will attempt to pin the player down with suppressive gunfire whilst one of their buddies tries to sneak round and cap him. If the "sneaky enemy" always took a direct route to the player, this would not be such an effective tactic since it would very easy for the player to predict where the "sneaky enemy" would pop up well in advance. In actual fact the "sneaky enemy" will circle round the player in a randomised fashion as he closes in, which makes it difficult to predict exactly where he will pop up. Also, sometimes the "sneaky enemy" will not actually reach the player, but will secretly take cover in a position flanking the player. So when the player runs for cover he thinks he is safe but he will actually find an enemy waiting for him.
Playing a multiplayer game against human opponents is fun partly because human opponents are more skilled than all but the best AI, but also because humans are devious, sneaky and will try to outsmart you. If you make an AI controlled enemy unpredictable then they effectively can outsmart the player by catching them off guard, and if an AI battle is different each time you play, your game won't get boring after the first play.
==Response to other NPCs==
This is where it gets tricky. If you want the AI to be able to battle each other or work as an effective team, they will need to be aware of each other as well as the player.
I'll explain why this is complicated: if each NPC has to consider the player and the player only, then the loss in performance will be approximately proportional to the number of NPCs.
If each NPC has to consider both the player AND every other NPC, then the loss in performance is approximately proportional to the number of NPCs SQAURED.
So if the NPCs react to each other as well as the player, the performance can potentially drop (increasingly) very quickly as more NPCs are added.
I have found a useful technique for getting around this problem. The basic code is like so:
Always- Set a to (a+1) mod n
Always- Spread value 0 in value A("NPC")
Always- Set value A("NPC") to value A("NPC") mod n
If value A("NPC") = a Then <Perform AI code for that NPC>
The function of this code is to spread the AI calculations across multiple MMF loops. Say if you have 9 NPCs, setting the value of n to 3 would mean that 3 NPCs are AI tested per loop, as opposed to 9. So just by this simple piece of code, AI is now using 3 times less processor power. In general, the performance hit due to the AI will be approximately proportional to 1/n. The downside of this is that an NPC could take up to n MMF loops to become aware of a change in the situation. 1 MMF loop takes 1/50 seconds milliseconds at 50fps, so if you set n to 50, NPCs would take up to 1 second to react to changes in their situation. I'd say that a fair balance between performance and AI functionality would be around n=10.
Creating an engine where NPCs are fully aware of each other is very challenging, especially in MMF, where performance is a big issue. However you can get away with making the NPCs only partially aware of each other, mainly focussing their attention on the player.
In my shooter, the enemy troops often provide cover fire for each other. When an enemy troop wants to flank the player he will send a message to the three random troops near to the player instructing them to open fire. This is fairly simple to implement; when an enemy switches to "flank" status then three enemies are picked at random, on the condition that they are at a distance less than 250 pixels from the player (this distance is changed to 150px for close quarters indoor areas), and provided they are not currently attempting a flank manoeuvre themselves.
Hopefully what you will take away from this article is that AI's three main weapons are awareness, response and surprise!
Remember, it is up to you to think very carefully and extensively about which specific things in your game the AI needs to be aware of, and how they should respond to each situation. Its always useful to "Role-play" your game; think if you were an NPC in a given situation, what would you do?
Also be careful about what parameters you randomise in order to make your AI unpredictable. The patience of an NPC (how long they will wait before breaking their cover pursuing or attacking the player) is always a good one, also their direction of movement.
It's good to not go too crazy with randomisation, for example, I wouldn't randomise NPC health at all because it doesn't really enhance the gameplay in any way. Maybe in real life one guy would be slightly tougher than the next, but if the dice rolls such that the player winds up fighting a legion of super brawny enemies, then there’s not a great deal they can do about it except hope they get dealt a better hand next time round.
In the end it all comes down to meticulous testing and fine tuning of your AI. You should aim to design your game so that the player can always win the fight if he makes the right decisions and has an appropriate level of skill. Losing due to random misfortune in a game is not fun and should be avoided! (Unless you are writing AI for a poker game) In practice your AI will never behave exactly how you want it to first time round, so test, adjust, retest and repeat until it is just right.
Hope this helps somebody.