[In latest installment of his series on the combat design of Uncharted 2, Naughty Dog combat designer Benson Russell delves into the techniques the studio devised for its combat AI, and how those evolved from the original game in the series. Part I discusses how the team evolved the combat design from the original Uncharted, and Part II takes a look at combat encounter design.]
I wanted to share some of the nitty gritty technical details of our AI systems to give insight as to how we tackled some of the common AI issues we all face as developers. Most games have some form of AI and regardless of the genre of game, there are fairly common requirements that have to be met. So I think you should be able to relate a lot of this to the systems you're working with, and perhaps spark an idea or two on new things you could try.
Our AI comprises a bunch of very modular systems that handle the different aspects of what we need them to do. In high-level terms:
- There are designer interface components that allow us to define the roles and behaviors for gameplay.
- There are aesthetic components that handle how they look and animate.
- There are decision-making components that pick where to go, how to get there, and what to do along the way and once there.
We try to keep the systems as modular as possible as it allows us to rapidly experiment and prototype with new ideas and gameplay mechanics. It can cause some issues when the systems need to cross-communicate, but the benefits far outweigh the negatives.
For the purposes of this article, I want to stick to what we mostly deal with from a designer's perspective, so this is what I'll be giving the most attention.
The way we define an AI type is by creating a set of default data for all of the various AI systems. It's very open-ended, to allow us the ability to create almost any kind of behaviors we want without having to hardcode them into the engine via programming support (although we can go that route for special cases). We use our custom scripting language as the primary interface for setting these parameters, as well as interfacing with them on the fly during gameplay. Let's take a closer look at how we set up an AI:
So at the highest level of defining these settings we have what is called the Archetype. This forms the starting point for the default set of data to be used when this particular type of AI is spawned in the game. We create the Archetype and then assign it to an AI game object that is placed in a level. For example, here's the Archetype that defines a light pistol soldier from Uncharted 2 (the guys with a light grey uniform wielding a Defender pistol, to be specific):
:weapon-skill-id (id 'pistol-basic-light)
:targeting-params-id (id 'default)
It's a simple structure that contains all the data needed for an AI to know what their abilities will be. There are more options that can be included than what this example shows, but these are the basic meat and potatoes of what will make an AI tick.
You can see one of the first pieces of data (and probably one of the most important) is the Skills list. This is the heart of our AI in terms of what abilities they can have. It's designed to be modular so skills can be added and removed with simplicity. Here are some descriptions of the skills used in this example:
- Script: This allows the AI to be taken over via our scripting language. It basically turns their brain off and allows them to respond to script commands. We use this in cases where we need the AI to play a particular animation, or we want to order them to move to specific points. This is needed as without it they would still be thinking and trying to act on their own, thus they would end up fighting against the commands issued to them.
- Cover: This gives them the understanding of cover and how to use it (I'll be going into details of our cover system later on). For characters where we don't ever want them to take cover, we can simply remove this skill.
- Open-Combat: This tells the AI how to fight out in the open outside of cover.
- Turret: This allows the AI to use mounted turrets, both in terms of gameplay and technically (i.e. what animations to play).
- Idle: This gives them an understanding of idle behavior (i.e. when there's no enemy in the area).
- Patrol: This allows the AI to understand how to use patrol paths that we lay out in our custom editor called Charter.
- CAP: This stands for Cinematic Action Pack. Action Packs (AP) in general are how we get the AI to perform certain animations for specific circumstances (such as vaulting over, or jumping up a wall). The CAP is a way to have an AI execute a desired performance (like smoking a cigarette) but still have their brain on so they can respond to stimulus. The CAP would define all of the needed animations, as well as transitory animations to get the AI into and out of the different states.
There are many more skills than the ones I went over here, and each is designed as a modular nugget to tell the AI how to react for various gameplay purposes. This makes it very easy to add features to the AI as the programmers can just write a new skill that plugs into the list. We also have the option to dynamically add and remove skills on the fly during gameplay through our scripting language.
The Health parameter should be self explanatory. We specifically switched from a floating point value used in Uncharted 1 to an integer value in Uncharted 2. The reason for this is we were getting some unpredictable behavior out of the floating point system (i.e. checking for a value of 1.0 but it's coming in as 0.999999).
The main end result to the player was that sometimes enemies would take an extra bullet to go down. Also to keep things simple we try to give everything a base of 100 Health (AI and player). There are a few exceptions when we want a really powerful enemy (such as the heavy chain-gunners), but for the most part we want the consistency so it's easy to balance (a little more on this further down).