Gamasutra: The Art & Business of Making Gamesspacer
The Secrets Of Enemy AI In Uncharted 2
View All     RSS
November 14, 2018
arrowPress Releases
November 14, 2018
Games Press
View All     RSS
  • Editor-In-Chief:
    Kris Graft
  • Editor:
    Alex Wawro
  • Contributors:
    Chris Kerr
    Alissa McAloon
    Emma Kidwell
    Bryant Francis
    Katherine Cross
  • Advertising:
    Libby Kruse






If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 

The Secrets Of Enemy AI In Uncharted 2


November 3, 2010 Article Start Previous Page 2 of 5 Next
 

AI With Class

The Character Class sets the look of the character in terms of their animation sets. The classes are defined under the hood between the programmers and animators. For Uncharted 2 we had Light, Medium, Heavy, Armored, Shield, Villager, and SLA classes (as an interesting side-note, SLA stands for ShangriLa Army, which were the people mutated by the sap from the Tree Of Life that Drake fought at the end of the game).

Damage

The Damage Receiver Class is the main way we balance the game in terms of how much damage the enemies can take from the different player weapons. This is an open-ended system in that the designers have the power to set up everything however they choose without having to rely on the programmers.

We implemented this system in Uncharted 2 because we kept running into issues where we wanted to tune the different weapons against different types of enemies.

Originally for Uncharted 1 (as with most games I've worked on) weapons did a set amount of damage against everything, which you tuned by setting the health of everything. This tends to cause many headaches when it comes to balancing.

For example, if you decide to change the damage of a weapon, it would have a spiraling effect on having to re-balance almost everything because you would have to re-adjust the health of all the enemies, which in turn means changing the damage of other weapons, etc...

It also causes for a lot of confusion in that enemies would have seemingly random amounts of health upon inspection (i.e. enemy A has 90 health yet enemy B has 400?!)

So to solve this, we created the Damage Receiver Class system. It starts with a simple spreadsheet that designers can set up, and then we run a Python script to convert it into our game script. It looks like this:


(click for full size)

The columns represent the weapon names (these are under-the-hood names that designers have set up elsewhere), and the rows represent the different Damage Receiver Classes. The intersection of each row and column represents how much damage a particular weapon will do against a particular Damage Receiver Class.

So looking at the spreadsheet for example, "pistol-semi-a" (which is the Defender pistol) will do 34 damage against anything that has a Damage Receiver Class of "light-class," and 17 damage against anything with "medium-class." We can create as many Damage Receiver Classes as we need and assign them accordingly (and not just to AI). This makes balancing the game very straightforward and simple.

Weapon Skill ID

This is how the AI understand the weapon they've been given and how it's supposed to operate. It's a simple structure that defines how the weapon will behave in terms of accuracy, damage, and firing pattern. We purposefully separate the player's version of these parameters so we can customize how effective the enemies will be with any particular weapon. Here's what one would look like:

(new ai-weapon-skill
:weapon 'assault-rifle-b
:type (ai-weapon-anim-type machine-gun)

;; damage parms
:character-shot-damage 5
:object-shot-damage 120

;; rate of fire parms
:initial-sequence-delay (rangeval 0.0 0.0)
:num-bursts-per-sequence (rangeval-int 10000 10000)

:auto-burst-delay (rangeval 0.4 0.8)
:auto-burst-shot-count (rangeval-int 3 5)

:single-burst-chance 0.33
:single-burst-delay (rangeval 0.4 0.8)
:single-burst-shot-count (rangeval-int 1 3)
:single-burst-fire-rate (rangeval 0.16 0.20)

;; accuracy parms
:accuracy-curve *accuracy-assault-rifle-upgrade*

:time-to-accurate-cover 3.5
:accuracy-cover-best 1.0
:accuracy-cover-worst 0.0

:accuracy-running 0.9
:accuracy-rolling 0.5
:accuracy-standing 1.0

:master-accuracy-multiplier 1.0
:master-accuracy-additive 0.1
)

At the top there's some key information regarding the visuals. The Weapon parameter references the internal game name of the weapon so it knows what art, FX, and sounds to bring in. The Type parameter sets the animations to be used (either pistol, machine-gun, or shotgun). The Damage section I think is self explanatory, and again we use a base health of 100 for the player.

The Rate Of Fire parameters defines how the AI will use the weapon, and it is here that we set up the firing patterns. There are several key issues we have to keep in mind while setting these up. The first is the actual volume of bullets as that effects how much the player can be damaged. The second is the actual behavior of the weapon (i.e. making sure a fully automatic weapon behaves as such).

The third is how the audio will be perceived in combat. This is an issue that we're constantly at odds with trying to balance. We want the AI to fire the weapon in a realistic manner as to what a normal person would do, but we also don't want every AI in a combat space firing fully automatic weapons without any pauses.

It not only doesn't feel right and breaks the immersion, it just sounds plain horrible! The cacophony of that many weapons going off at once overloads the system and instead of an intense firefight you get distorted noise. It also makes it more difficult for the player to be able to identify what weapon an enemy is using.

The way the Rate Of Fire parameters work is that there's a firing sequence with an optional delay between each sequence (in this case it's not being used). A sequence consists of a number of bursts (in this case set to 10000), and each burst can either be fully automatic (in which case it uses the fire-rate set by the weapon) or a set of single shots with a manually entered fire-rate.

So in the above case each burst has a 33 percent chance of being a set of single shots, fired at a rate of .16 - .2 seconds between each shot, and a delay of .4 - .8 seconds before the next burst fires. Else it will be a fully automatic burst consisting of 3 - 5 bullets, and a delay of .4 - .8 seconds before the next burst.

The accuracy curve points to a pre-defined structure that looks like this:

(define *accuracy-assault-rifle-upgrade*
(make-ai-accuracy-curve
([meters 60.0] [chance-to-hit 0.0])
([meters 30.0] [chance-to-hit 0.3])
([meters 20.0] [chance-to-hit 0.4])
([meters 12.0] [chance-to-hit 0.4])
([meters 8.0] [chance-to-hit 0.5])
([meters 4.0] [chance-to-hit 0.9])
)
)

The way this reads is it's a set of points on a graph that define the accuracy of the weapon at a specified desired distance, with a linear ramp between each point. So in this case, from 0 - 4 meters there is a 90 percent chance of hitting, which then falls off to 8 meters where there is a 50 percent chance of hitting, which then falls off to 12 meters where this a 40 percent chance of hitting, etc... We purposefully set these accuracy curves up separately so we can easily re-use them in any number of weapon skills that we choose.

The rest of the parameters are modifiers that effect accuracy and should be mostly self explanatory. The one I'm sure you're wondering about is the Time To Accurate Cover section. What this does is allow us to give the player a small window of opportunity when they pop out of cover to take a shot. It will lower the current accuracy of the AI by the percentage listed in Accuracy Cover Worst (in this case to 0 percent), and then over the duration set in Time To Accurate Cover (in this case 3.5 seconds) it will ramp the accuracy up to Accuracy Cover Best (in this case 100 percent).

So the net effect for the player in this case is when the player pops out of cover to take a shot they have 3.5 seconds where the AI's accuracy will go from 0 percent back up to whatever it should be based on conditions.

By using these weapon skill definitions, we are able to make the different types and classes of AI more or less challenging. As an example, for Light class soldiers overall we gave them slightly slower fire rates and a longer Time To Accurate Cover to make them easier.

With the Medium class of soldiers we would increase their fire rates, boost their accuracy by 10 percent with the Master Accuracy Additive, and reduce their Time To Accurate Cover to make them tougher. We would also try to tailor the fire rates of different weapons to make them sound a bit more unique so as to be more easily identified by the player (i.e. a FAL would have a different pattern than the AK and the M4).


Article Start Previous Page 2 of 5 Next

Related Jobs

Zapdot
Zapdot — Cambridge, Massachusetts, United States
[11.13.18]

Software Engineer
Zapdot
Zapdot — Cambridge, Massachusetts, United States
[11.13.18]

Senior Software Engineer
Monster Squad
Monster Squad — Seattle, Washington, United States
[11.13.18]

UE4 GAMEPLAY PROGRAMMER
Sixteen Tons Entertainment GmbH
Sixteen Tons Entertainment GmbH — Tuebingen, Germany
[11.13.18]

Unreal Engine Developer / C++





Loading Comments

loader image