A common problem for game AI programmers is how to distinguish the behavior of different AI characters. As an example, how do we make an orc and a dragon genuinely different as opponents? We can increase the health, armor, and damage of the dragon, but unless we give it a different personality in some way, and make it behave differently, it might as well just be a very strong orc.
The techniques described in this article are applicable to a wide variety of game types. I have applied (and refined) them over the course of developing a console FPS (Men in Black: Crashdown), a PC and console board game (Monopoly Party), and now a PC and console, real-time, turn-based, strategy/action game (Worms 3D). The specific methods described are most applicable to games which have a variety of different kinds of AI agents. The most obvious examples of games requiring agents with differing behaviors are probably RPGs, with various races of enemies, or character classes; and FPSs with various species/types of opponent.
This article will present a flexible, robust, and easily extensible solution to the problem of how to give your AI agents a rich palette of behaviors, as well as a sense of character and personality.
Same Code, Different Data
A much-discussed concept in game programming is writing systems to be "data-driven" (see references at the end of the article), and this is a perfect place to put the concept into practice. Essentially the idea is to program your system to work according to data fed into it, so that the same code can produce very different results by changing the parameters it is given. The advantage of coding this way is that program behavior can be changed without altering the code at all -- by simply altering the data it is fed. This means that agent behaviors can be tweaked, or even completely changed or created, without having to edit and recompile code, interrupt a programmer (it can be done by a level designer), or even exiting from the game, if you include the ability to reload the data the engine is using on the fly.
The disadvantages of writing a system to be data driven, instead of using hard-coded values, is that it will run marginally slower (although this will be negligible for the systems described in this article), but that more importantly, you will need to spend a bit longer up front, designing and coding your system. Even starting from scratch, if you are writing a game that will have several types of AI characters, the time you save in the long-run by designing your system to work in this way will far outweigh the extra time required at the beginning. (See the sidebar, "Data Format", for a discussion regarding what form your data should be in.) My hope is that this article should give you a head-start with designing your system, and cut down the time you'll spend up front.
Sidebar: Data Format
An important question is the format used for the personality data files. They must be backwardly compatible, so that when you add a new parameter, it won't invalidate all the currently existing files. Backwards-compatibility can be implemented by supplying default values for any new parameter. If the parameter is not explicitly mentioned in a data file, the default value is used. It may also be useful to allow data files to inherit from other data files, and then override the specific parameters which need to be different for the new character.
I would advise a text-based, human-readable format for development (as an aid to debugging). If the time taken to parse these files is found to be significant, a release version can be provided with the files pre-processed to reduce the time needed to use them on the fly. The data files should certainly be kept in your version control system, as well as your source code.
In the examples I have used a simple "token = value" syntax, with "START block ... END block" as block delimiters, although other schemes could of course be used, such as XML. It can be an advantage to provide a tool to edit these data files. Such a tool could provide drop-down lists of all animations applicable to the specified mesh, or of all possible behaviors, or it could include a "wizard" that fills in all the parameters with their default values for a behavior when it is added. Naturally, if others are going to be editing data files, it will be advisable to provide some documentation--at least detailing the uses of the different parameters and their valid range of values.
An important thing to realize about data-driving is that well chosen parameters can combine in unexpected ways to give greater variety than was originally visualized. In other words, if you give the power to get your code to do different things to other people, then they will find ways of making it do things you never intended it to do. This can be bad -- it will certainly expose some bugs in your code -- but it can also be very good, as a flexible enough system will allow the designers feeding your system with data to exercise their imaginations, without being limited by yours.
But a dragon and an orc shouldn't just have different health and armor; they should behave differently. Of course, in a full-blown game orcs and dragons will have different models, animations, visual effects, sound effects, rewards for defeating them, and so-on, but if a dragon just behaves like a very tough orc, the game world will inevitably feel flat and artificial. Encountering the two types of opponents should be very different experiences, and should require very different kinds of response from the player.
The next aspect of most AI characters that we'll consider has already been mentioned in Example 2, namely its weaponry. Most AI characters in games have weapons of some sort, except of course those guys who like hanging around in taverns or star-ports, even if it might be a claw or psion-blast, instead of a gun or axe. Naturally then, we'll want parameters for the weapons an agent has. The first ones you'll think of are probably "damage", "range", "accuracy", and maybe "time between hits", but for a full-blown game you'll want to include a lot more. The weapon may have a separate model made for it (if that kind of agent can use more than one weapon), in which case you'll want to include the name of the weapon model as one of your parameters rather than having a big hard-coded switch statement somewhere saying which weapon model each character uses. But don't stop there. How about having parameters for the animation to be played when firing the weapon, and the name of the particle effect when it is fired, and when it hits? Don't forget all the sound effects as well.
Maybe all these extra parameters sound like a lot of hard work? Wouldn't it be easier to just hard-code the names of the assets in? It certainly would be easier for a game just using two or three kinds of characters, but if you want to have more, and you want others to be involved in creating them (which will probably improve your game in the long-run), then it will save huge amounts of time to encode all this information into your data file. If you do, then someone else will be able to add a weapon, with all the graphical and audio assets, without ever bothering you.
you'll want to have the details of all the assets (models, animations,
sound effects, particle effects, etc.) for the actual agent included
in the data file too. Strictly speaking, these sort of things aren't
part of the AI, but it certainly makes sense to put them all together
from the point of view of game content creation. Before long you'll
be playing the game against AI opponents that you've never seen
before, and had no immediate hand in creating, even though they're
using your code. If your design teams are talented and can get to
grips with the tools you provide them with, this can be a very exciting