It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.

Search articles, jobs, buyers guide, and more.

By Paul Tapper
[Author's Bio]

Gamasutra
December 3, 2003

Introduction

Extensible Parameters

Debugging and Logging

Printer Friendly Version
   

 

Change Login/Pwd
Post A Job
Post A Project
Post Resume
Post An Event
Post A Contractor
Post A Product
Write An Article
Get In Art Gallery
Submit News

 


 


[Submit Letter]

[View All...]
  



Upcoming Events:
IGDA Leadership Forum
Burlingame, United States
11.12.09

GameSoundCon
San Francisco, United States
11.13.09

Southwest Gaming Expo
Dallas, United States
11.20.09

Workshop on Network and Systems Support for Games (NetGames 2009)
Paris, France
11.23.09

EVA 09 - Exposicion de Videojuegos Argentina
Buenos Aires, Argentina
12.04.09

[Submit Event]
[View All...]

 


[Enter Forums...]

Note: Discussion forums for Gamasutra are hosted by the IGDA, which is free to join.
 

 

 


Features

Personality Parameters:
Flexibly and Extensibly Providing a Variety of AI Opponents' Behaviors

Extensible Parameters

We've just considered adding parameters for weaponry in our data file, but what should we do about characters with more than one weapon? We could provide slots for a fixed maximum number of weapons, which may or may not, all be utilized for any particular character. However, a more flexible solution would be to encode our parameters in a format which would allow us to arbitrarily add as many weapons as we wanted (see side-bar). If the format is well thought through it should allow us to add extra sound effects for more variation in the sound of the gun fire, or the footsteps, to add extra idle animations, and also to add extra behaviors. But we'll come on to that later.

As an example of where we've got to, with the parameters we have so far, it would be a simple matter for someone else to take the parameters for an orc and create a new parameter file for an orc commander who has a different model, some different animations and sound effects, better armor, and a crossbow as well as the standard sword.

Decision Parameters

Figure 1.

There is one problem with our orc commander though: how does he know which weapon to use at any particular point in combat? The problem is, of course, increased as we add extra weapons to a character, and if we're going to be adding weaponry at will, we'll need a very flexible solution to this decision problem. A mechanism I have found to be very effective is, given a particular choice for an AI character, score each of the alternative options, and whichever has the highest score is the winning choice. The most basic scoring system would be to just randomly choose any one of the available options. This would be very simple to implement, and would show off the versatility of the character, but they would quickly be seen to be pretty unintelligent.

A better method would be to have the score for an option dependant on the current situation, perhaps using compound fuzzy switches (products of values between 0 and 1) -- see example 4. Another option, which could be used along with fuzzy switches, would be to access script functions (if your game has a scripting language) passing a reference to the character, along with whatever other information might be helpful, and getting back a value from 0 to 1 which can be multiplied with all the other fuzzy switch values. A common problem with decision making, or state changes, in AI characters, is that it is sometimes very predictable to the player where the decision boundary is. To avoid this predictability it is easy to multiply each option's score by some random scale whenever they are assessed (perhaps between something like 0.8 and 1.2), thus making the boundary between choices a little more blurred.

Behavior Parameters

Figure 2.

The decision parameters above leave some questions un-answered. When should the character reassess the weapon it's trying to use? How does the character know how to use a particular weapon (e.g., move in close for a sword, pull back to the best range for a crossbow)? Also, we'd really like AI agents that are able to do more than just attack the player. Even if they can use more than one weapon, just flying into battle all the time would make our characters very predictable, and rather boring--certainly not very believable as thinking beings. We'd like our characters to attack, side-step, fall-back, run-away, fire from cover, ring an alarm bell, etc. My favorite little AI behavior was demonstrated by the lower-level soldiers in the original PlayStation game Medal of Honor, who dove on a grenade you just threw at their feet, attempting to protect a nearby officer. It also adds immensely to the player's belief in the characters if they aren't always standing still in the same corner waiting for him, but actually seemed to have a (limited) life of their own before the player turned up. Halo is an excellent example of this, in which Covenant aliens are always in a slightly different place and doing something slightly different every time you restart from a check point.

Figure 3.

All the above behaviors, and as many more as you can think of, can be flexibly added to our AI characters using parameters in our data files. In the same way as we added arbitrarily many weapons to our characters, we can add an arbitrary number of behaviors as well. Each behavior can be given a "type" value which defines which pieces of code will be run to carry the behavior out. Depending on the behavior type, there will be other parameters we may wish to add to customize the behavior more accurately for the character in question. For example, we may have a "useWeapon" behavior, which can take a "range" parameter, which defaults to hand-to-hand range, but can be set to whatever we want. We can then use the range parameter to set the distance from which our orc commander will try to fire his crossbow bolts. If we just give a "range" parameter though, we may have the problem that any orc commander in our game will always go to exactly the same distance away from the target before firing, damaging our illusion of thought. One way around this predictability would be to build into the "useWeapon" behavior, variability in the range the character tries to use a weapon from (so it takes the "range" parameter and multiplies it by a random number between 0.8 and 1.2). More flexibly, we could provide a parameter in the data file which specifies the variability in the range, which defaults to +/- 0.2, but could be overridden to be whatever we want for that character.

In Halo, you never know where Covenant warriors might show up or what they might be doing when you run into them, giving you the sense that the creatures have things to do besides killing humans.

For each behavior, we can provide a set of decision parameters (as described in the previous section). Whenever our character then needs to choose a behavior to carry out, it can score each available behavior according to the current situation, and action the behavior with the highest score. We'll want as rich, but meaningful, a set of decision parameters as possible to help our character decide between possible actions. The more carefully we set up the decision parameters, the more intelligent the character's choices will appear to be. Not all the decision parameters need be purely dependant on the current state of the game world though. As an example, in Worms 3D, the AI certainly considers many different world conditions when choosing between plans, but also takes into account things like whether it has tried a similar plan before, and failed, and even whether the weapon it is thinking about using has been used recently (to help add variety to the game experience, and avoid the AI getting into a rut)--of course these two considerations may well not make sense in all game types. See figures 1-3 for some debug graphics illustrating the AI in Worms 3D considering alternative plans.

An obvious question, begged by adding decision parameters to our behaviors, is when should we score all our behaviors to choose a new one? The answer certainly isn't every frame. Re-scoring behaviors every frame will, of course, be slow, particularly if we have many agents active concurrently. But it also introduces the danger that we may end up with schizophrenic characters, who constantly change their minds about what they want to do and vacillate between different actions. A more natural system would be to reassess the characters' behaviors when an action terminates, and also every quarter-second to second (again I'd recommend a randomness factor in the time between reassessments). This will mean that if the situation around the character changes, in a way they should respond to, they will take note fairly quickly, but there will be a short unpredictable "reaction-time" built into the system. A great advantage of assessing the behaviors so infrequently (roughly every 15 to 60 frames in NTSC) is that the system can be designed to stagger the processing between characters, deferring the calculations for a character to the next frame, if another character has already been assessed this frame. Use of this, and similar techniques, can smooth the processor load from the AI to a great extent, allowing you to get more thinking done, with less frame-rate hit, than an un-smoothed system--but that's a different topic.


Example 1

As a very simple example, consider the two parameters "health" (or hit-points) and "armor" (an amount taken off any damage done to the character). Obviously, to make a weak character (e.g., an orc) you give it low health and low armor. Conversely, for a strong character (e.g., a dragon) you give them oodles of health and high armor. What could be simpler? But what about a Jabberwocky with low health, but very high armor. This character could decimate thousands of villagers unscathed, but would fall to one good hit from a high-rank Paladin, something which the programmer may not have foreseen when she originally wrote the system.

To give an idea of what our starting data files might look like at this point, we might have two files, "orc.dat" and "dragon.dat" with "orc.dat" containing:

name = "orc"
health = 10
armor = 1

and "dragon.dat" containing:

name = "dragon"
health = 500
armor = 10

Example 2

Probably the next most obvious, and easiest, parameters to add to your data file for an agent are its movement characteristics. For example: "forward speed", "side-steep speed", "backward speed", and "turn speed". These values will certainly want to be different for different kinds of agents, and their differences will immediately cause them to behave in distinguishable ways and require different tactics from the player. As an example, a high forward speed but low turn speed opponent will encourage the player to use circle-strafing (in an FPS), whereas a high turn speed but low forward speed opponent, with close range weapons, will elicit playing styles involving the use of ranged weapons, and repeated falling back.

One particular alien race in Men in Black: Crashdown (the Kayzor) was a smallish crab/scorpion type thing, with one eye, which came alive during development when it was given a slow forward speed, but a very fast sideways motion. This tended to mean it was always circle-strafing the player, and made it an unusual and challenging enemy, especially in packs.

Example 3

As an example of how our orc commander data file might look, we might have a file "orc_commander.dat" containing something like the following:

name = "orc commander"
model = "orc_commander.mdl"
health = 20
armor = 3
moveForward = 1.3
...
START weapon
   name = "sword"
   model = "orc_sword.mdl"
   damage = 6
   range = 5
   animation Idle = "orc_hold_sword_breathe.ani"
   animation Attack = "orc_attack1.ani"animation
   Attack = "orc_attack2.ani"
   ...
END weapon
START weapon
   name = "crossbow"
   model = "crossbow.mdl"
   damage = 4
   range = 120
...
END weapon

with "..." meaning that some of the imagined file has been omitted for brevity.

Example 4

Given our orc commander with a sword and a crossbow, we could encode the decision about which weapon to use in a situation, in the following way:

FS_range = FuzzySwitch( range,
                 weapon.rangeScale,
                 weapon.rangeBase )

FS_armour = FuzzySwitch( armor,
                 weapon.armorScale,
                 weapon.armorBase )

Score( weapon ) = FS_range * FS_armor

where range is the distance of the target from the character, armor is the armor of the target, weapon.parameter means the value given to parameter in the description of weapon in the character's data file, and

FuzzySwitch( val, scale, base ) =
   clamp( (val - base) * scale, 0.0, 1.0 )

Each weapon could be scored using its parameters from the data file, and the one with the highest score is chosen for use. Example values might be:

sword.rangeBase = 100
sword.rangeScale = -0.01
sword.armorBase = 0
sword.armorScale = 0.1

crossbow.rangeBase = 0
crossbow.rangeScale = 0.01
crossbow.armorBase = 5
crossbow.armorScale = -0.2

In the situation with an opponent who is 20 units away and has armor of value 3, we'd have Score( sword ) = 0.8 * 0.3 = 0.24, and Score( crossbow ) = 0.2 * 0.4 = 0.08, meaning the sword gets selected. With an opponent further away, say 90 units, and less armor, say 1, we'd get Score( sword ) = 0.1 * 0.1 = 0.01,and Score( crossbow ) = 0.9 * 0.8 = 0.72, meaning that the crossbow gets favored.

Example 5

Putting together everything we've discussed, a very abbreviated personality data file for an orc might look something like the following:

name = "orc"
model = "orc.mdl"
health = 10
armor = 1
moveForward = 1.1
...
START weapon
   name = "sword"
   model = "orc_sword.mdl"
   damage = 6
   range = 5
   animationIdle = "orc_hold_sword_breathe.ani"
   animationAttack = "orc_attack1.ani"
   animationAttack = "orc_attack2.ani"
   ...
END weapon
START behavior
   name = "attack with sword"
   type = "attack"
   weapon = "sword"
   scoreRangeBase = 100
   scoreRangeScale = -0.01
   scoreArmorBase = 0
   scoreArmorScale = 0.1
   ...
END behavior
START behavior
   name = "run away"
   type = "moveAwayFrom"score
   HealthBase = 10score
   HealthScale = -0.05
END behavior
...

 

______________________________________________________


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2003 CMP Media LLC

privacy policy
| terms of service