An Overview of the AI Architecture
Inside the F.E.A.R. SDK

In theory, there’s no difference between theory and practice. Of course, in practice when you build a game AI engine, things turn out very differently.

PC games have always been a great source of inspiration for enthusiasts, as they often allow you to look under the hood to see what’s going on. It’s great when the developers release the original SDK, but it’s even better when it’s packed full of AI technology!

This article looks into the latest SDK behind F.E.A.R., the hit first-person shooter acclaimed for its artificial intelligence. The codebase is in C++, like the rest of the AAA games industry, but the code is written in a style that should provide valuable insights for the developers among you using C# or Java.

Downloading and Installing the SDK

A fully working installation of F.E.A.R. is required to install the SDK. Here’s how you should proceed:

  1. You need an original copy of the game. The F.E.A.R. trilogy is particularly good value for money for AI enthusiasts.

  2. Make sure the game is patched to the latest version, 108. Go to this FTP directory and pick the right update for your language.

  3. Now get the F.E.A.R. public tools version 108. This is the latest version of the SDK so it should match the latest patch also.

For those of you that already own the game and don’t have 1 GB of bandwidth to spare, I’ve made a backup of the engine and game source code and attached the package to this thread in the forums. (You’ll need to register to download it.)

Once the installation is finished and all the files have unpacked, you’ll have a .\Tools\ directory — typically within your game folder.

F.E.A.R. Source Code Development Kit SDK

Screenshot 1: A sample of the files available freely in the SDK.

Finding Your Way Around the Codebase

You’ll find a Visual Studio solution file called Game.sln file within the ./Source/Game/ directory. The free Express edition of Visual C++ is suitable for opening that file.

The AI code lives in the ./Source/Game/ObjectDLL directory, along with the rest of the game logic that runs on the server. Predominantly in that directory, you’ll find:

  • Actions, Goals, Sensors and Nodes — Most of the files are modular tasks for gathering information (sensors) and making changes to the world (actions), using objects in space with animations (nodes), and evaluating objectives and for the AI (goals).

  • AI Framework & Algorithms — The other files starting with the prefix AI contain the supporting code for these specific objects listed above. The SDK also contains the implementation also contains the necessary algorithms for making decisions.

  • Game Logic — The rest of the files are the typical game DLL code, with the application logic, code to manage the client/server, etc.

Of course, the SDK doesn’t give you complete access to all the engine code, but there’s a surprisingly large amount of the AI code fully available. Either way, it’s more than enough to get an idea of how the architecture is structured.

F.E.A.R. AI Character

Screenshot 2: An AI character in F.E.A.R. ducking for cover.

Overview

There are a few things you should look out for in particular.

The Planner
F.E.A.R. is famous for its use of planning technology! You get access to a surprisingly large part of it in the SDK. It’s implemented at the core as A* in AIAStarMachine.[h,cpp], which serves as the basis for an object-oriented design including the goal planner (in AIAStarPlanner.[h,cpp]) as well as the pathfinder (in AIAStarNavMesh.[h,cpp]).
Architecture
While you can find similar STRIPS planners elsewhere, what’s interesting about this codebase is how it was integrated with the rest of the game engine. The files AIPlanner.[h,cpp] integrate the low-level A* algorithm by apply the actions over time. Also be sure to look at the way the goals are implemented modularly, based on AIGoalAbstract.[h,cpp], to control the outcome of the planning itself and compete for activation.
Navigation Mesh & SmartObjects
The planner is based on solid underlying systems, specifically the navigation mesh technology. For instance, you’ll find a navmesh generator for the raw polygons from the World Editor in all files called AINavMeshGen*.[h,cpp], as well as the code for handling different kinds of movement within the world in AINavMeshLink(Stairs|Jump|FlyThrough|DuckRun).[h,cpp]. The smart object code is stored in files derived from AINode.[h,cpp], integrating with the animation system.

The Main AI Class

F.E.A.R. C++ Class Hierarchy

Figure 3: The C++ class hierarchy in F.E.A.R.

Here’s a quick overview of the most important classes that compose the primary CAI character class:

  • The static blackboard is used as a software design pattern to help reduce coupling between the AI sub-systems. It provides a central place for modular pieces of code to store their data.

  • A working memory stores the AI’s observations about the environment. By having this separate world model, the AI can be duped and tricked by the player based on what it knows and senses.

  • The world state is a very simple list of attributes that are used to represent the current situation, as used by the planner to perform its reasoning.

  • The brain stores properties only, like roll distance or grenade throw times.

The remaining classes are a bit more self-explanatory. You’ll find some comments in the code, particularly in the lower-level classes, but be sure to browse around to get the big picture.

Download the AI source code from the SDK in this thread of the forum. Free registration required.

7 Comments ↓

#1 Saddist on 01.08.08 at 12:19 am

I loved this edition for your blog, do you plan on following up with more in depth issues for both beginners and advance users? Or will this be the just a post to get people interested and expect them to self study?

(Don't get me wrong, of course we have to self study)

I just mean, will you follow up this post with more later on?

#2 BrianL on 01.08.08 at 2:48 am

A few comments on the Fear AI for someone other than Jeff who worked with it -

The brain class is legacy; it is mostly redundant. In a previous incarnation of the AI system, it allowed designers to specify parameters which drove the behavior. These parameters were merged into the database records which drove most of the character parameters.

A goal of the blackboard was to reduce the data duplication (and thus hidden invariants) between behaviors. Decoupling was definitely a goal as well, just not the only one.

In theory, all AI types could be a single class with the implementation swapped out at the component level. At the implementation level, this is problematic if the per-instance designer configuration data is constrained to a statically exposed set of properties on the AI. More succinctly, the Fear tools expose properties per class type. As all AI are derived from a single class, it isn't possible to expose attributes which only apply to a subset of the AI.

The relationship between AIMovement and AINavigationMgr is too decoupled in some ways. AINavigationMgr handles path planning and sets a destination waypoint for AIMovement. AIMovement handles local pathing to the point. These classes execute in lock step; if the AI has 5 feet worth of movement in a frame but his next waypoint is only 1 foot ahead, he essentially 'loses' 4 feet worth of velocity. While separation can definitely be useful, this relationship could use some work.

For Fear, the AIActivities (which coordinated squad behavior and a lot of the communication) are worth looking at. You can think of the activities as 'automated scripting' in some ways. Long term, these will probably be expanded to become more dynamic.

The translation between behaviors and animation (via CAnimationContext) is powerful but difficult to manage. Basically, behaviors generate a pattern. AnimationContext uses the animation trees to find an animation matching the pattern. If no match is found, an error is issued. Insuring all pattern permutations have runtime matches can be a challenge - a simpler translation layer with a primary key and secondary data (ie play a motion behavior Jump, parameterized with information on the weapon/jump height/etc) would potentially be more robust and scalable to complex behaviors.

The AISoundMgr may also be worth a glance, as it handled prioritizing AI callouts.

The SmartObject terminology may be confusing. AINodeSmartObject and its children are designer placed and configured hints for the AI about the environment. These specify a SmartObject chunk of data which indirectly specified animation, behavior, etc. The SmartObject data chunks were eventually reused to specify animation/behavior for procedural behaviors like reloading a gun.

#3 alexjc on 01.08.08 at 6:09 am

Saddist,

Thanks for the feedback. There will be many more articles; see my introduction to the code studies column in the forums. I'll also go into more depth with this codebase... it's fascinating.

What level of expertize would you like to see?

BrianL,

Many thanks for your comment! That's really useful. I've only been reading the source for hours today, so I'm glad I didn't get anything too wrong...

If you're allowed to say, what did you use the F.E.A.R. engine for?

Alex

#4 BrianL on 01.08.08 at 12:18 pm

I was the AI programmer on Condemned: Criminal Origins. It was developed on the same code base as Fear. Our development started a little later; Jeff had the core planner and navmesh system in place before I started with it - the architecture is 95% his.

I was focused on the AI components specific to Condemned, optimization to make it run better on the consoles, cleanup, etc. Quite a few of the AI classes in the SDK were probably Condemned behaviors I authored that weren't actually used in Fear due to the code sharing methods.

After the games shipped, Jeff left for MIT and I moved to a lead programmer position. Most of my involvement with the AI the last few years has been in the form of training, feedback, review, high level direction, etc.

#5 Veksi on 01.08.08 at 1:51 pm

Oh! I must say this is the ultimate! Something real and concrete to build studies on.

So sad (too bad) there are only so many hours a day and some of them has to be used for sleeping. The next three months I'm super busy at work. I guess resigning and suspending my preliminary masters thesis plans aren't real options either. :-P

Thanks for this. I see if I'm able to spread out the good news.

#6 Saddist on 01.08.08 at 8:34 pm

alexjc,

In between. I'm not advance in this section. So carry on the way you do, but try to make it easy to follow. Not that any of your other writings are hard to follow. I guess I'll just ask questions along the way. And you'll be able to set the pace from there. That way, both beginners can cope, maybe with a little brain racking along the way.

#7 q on 01.15.08 at 4:29 pm

A very nice blog! Awesome webdesign too.

Leave a Comment

You can also reply to this thread in the forums.

Game AI Character