Game Development Community

dev|Pro Game Development Curriculum

Tactical AI Kit: Snake in the Grass

by Bryce · 12/16/2009 (1:32 am) · 18 comments

You're in a big, grassy field at dusk. It's foggy, you can only see out for about 150 meters. There are a dozen guards patrolling around you. You need to get to the other side of that field. Luckily, you're wearing your trusty ghillie suit (If you don't know what it is, it's a suit that makes you look like the ground. Google it!). So, you move slowly into the field of grass and crouch. Observing the patrol routes, you take out a scoped, silenced weapon and pop the nearest guard. Pause for response—none. You go prone and be on your way. Eventually, as you're crawling through the grass, the body of the guy you took out earlier is found by another patrol, and this patrol alerts others. The other ten guards are all pissed off now, making your job a lot more difficult. Since you're flat on the ground and you can't see much around you, you'll be screwed if another patrol happens to step on you. You could poke your head up and take a look around, but someone's probably going to notice that.

As lucky as you are, you hear the footsteps of an approaching guard. You stay absolutely still so he doesn't notice the movement. At this much closer distance, his odds of noticing you are a lot higher, and you still can't see him from your angle. Your only option is to stand up, fire away, and hope for the best. Good news, he's dead. Bad news, everybody noticed the commotion. The bullet impacts everywhere are enough of an incentive for you to hit the dirt. The guards open up on the field. Most of them are behind cover by now, the others are laying down suppressing fire, while you're crawling away. After a minute or so of not seeing you, they move in and sweep the field...scenario over. Everybody dies anyway.

Taking advice from my MGS-obsessed friend/tester, I worked on the AI visibility a bit. I've been playing around with new maps lately, mainly outdoor maps with terrains and all that fun stuff, and it gets rather aggravating when I'm laying down in the grass and a bunch of the hostile NPCs suddenly spot me.

My original implementation of AI visibility uses the Killer Kork method: Calculate a number above zero that represents how visible an object in question is. If the number is 0, the object is not visible. It's either outside the field of view of the NPC or outside its view distance. An object that is very close and in the absolute center of the NPC's field of view will give it a value of 1. If the object is firing a weapon without a flash suppressor (this is specified in the weapon's image datablock), the score goes up by a coefficient. If the object is moving, it gets multiplied even further. You get the idea.

I needed to make the AI easy (to a realistic level) without dumbing it down. To do this, I add more factors that affect visibility. At the moment, view distance+fog, foliage, and stance will all determine how visible an object is.

For example, let's look at the scenario I described earlier.

Fog: It's foggy out to 150 meters. If an object in question is between Sky.fogDistance and Sky.visibleDistance, it is in the fog, and will be given a value from 1 to 0 based on how much it is in the fog. 1 means completely visible, not in the fog, and 0 means out of sight.

Time of Day: It's dusk, so it's getting a little dark. There's a variable you can set on the Sky object called generalVisibility, which is a coefficient that is applied to all visibility calculations. This can be set to around 0.6 to limit visibility a little bit.

Foliage: Any fxFoliageReplicators inside a SimGroup called FoliageGroup will be calculated. Using the maximum height of each foliage billboard and the outerRadiuses (er, outerRadii?), it determines if the object in question is in a patch of foliage, and if the object is completely covered by this foliage (head to toe, so your best bet is to go prone in the shorter grass). You can save a variable called camoAmount on each patch of foliage, which is (surprise!) a number between 0 and 1 that affects the visibility score. For very thick grass, this can be set to a very low number, like 0.01.

Stance: I use Erik Madison's multiple player position resource to get players to crouch, stand, and go prone. Three variables can be set on your player datablocks (if you choose): StandVisWeight, CrouchVisWeight, and proneVisWeight. These do exactly what you think they do. The datablock for my ghillie-suit sniper gets low numbers for these, and an especially low number for the prone weight.

So basically, a sniper in a ghillie suit lying flat in a field with tall grass on a foggy day at night is virtually invisible. This allows for the dramatic, tense “Does he see me? Should I shoot him? Oh crap, someone's behind me” moments, and of course the moments where you feel powerful and ninja-like.

That's all, folks! And no, I don't know of a release date yet. So hush.


Leave a comment, or all of those chain letters you failed to pass on will get their vengeance.

#1
12/16/2009 (1:38 am)
neat! Release it already before someone else comes up with a similar kit. From what I can tell there are more than enough features. Not that I am eager to get my hands on the pack, or anything... dum de dum 8P
#2
12/16/2009 (3:19 am)
Sounds awesome, you are pretty much covering everything that I need in an AI system... cant wait for more news! :)
#3
12/16/2009 (8:05 am)
Quote:Foliage
Dang! I'd been wondering how to do that and you just told me exactly how I should go about it. Now I can't work it out for myself. *pout*

Sounds like good stuff! Vision is one of those important things for AI and player experience... it lets you play around in the unknown zone between stealth and detection.
#4
12/16/2009 (10:22 am)
Quote:
which is (surprise!) a number between 0 and 1 that affects the visibility score

I like numbers between 0 and 1.
0.8 is especially cool.
#5
12/16/2009 (1:01 pm)
yes visibility its very important for AI, I heard some people hating Battlefield Vietnam because AI can see throw foliage...
#6
12/16/2009 (5:46 pm)
Nice! I've gotta see that ghillie suit..

I must say, that sniper mission in Modern Warfare was my favorite. I'd make a whole game of similar missions if I just an AI kit of some kind. Hmmm...
#7
12/16/2009 (6:10 pm)
Forgot to ask - are you doing much with sound?
#8
12/16/2009 (6:33 pm)
@J.P.
img94.imageshack.us/img94/8382/ghillie.jpg
I'm not too sure how to model a ghillie suit, so this qualifies as "placeholder" art...and no matter what I do, the alpha channel shows up as black in the engine instead of being transparent

@Daniel
Not particularly. I like to have nice sounds here and there so it's not torture to my ears, but I'm mostly concentrated on AI and test maps.
#9
12/16/2009 (7:57 pm)
If I were a pro artist, I would have done ghillies the way they did the pinyata fur in Viva Pinyata. I remember reading an article about that somewhere... hmm. EDIT: On second thoughts, I think they may have used some exporter magic to automate the process.

Sorry, I should have been clear - I meant in terms of AI detection. I know you've worked with bullet sounds alerting AIs, but what about things like movement, etc? I remember one of the big points Crytek made abour Far Cry (or maybe it was Crysis) was that foliage and stuff being disturbed would make sound that could alert your enemies.
#10
12/16/2009 (8:42 pm)
The sound thing shouldn't be difficult at all. In the function that checks for foliage visibility, you could add a check to see if the object is close enough to be heard, and maybe make it random. In the foliage function, I could stick in something like this:

if (%inFoliage == true)
{
  %distToEars = VectorDist(%obj.getPosition(),%plr.getTrueEyePoint());
  if (%distToEars <= $Think3::HearPlayerDist)
  {
    %rand = getRandom(0,$Think3::ChanceOfHearingPlayers);
    if (VectorLen(%obj.getVelocity()) > $Think3::AudiblePlayerSpeed
       && %rand == 1)
    {
      if (%this.alertness == 0) // If non alert, investigate movement
      {
        %this.threatType = "Player"; // Tells the reaction code to 
                                     //  react to someone we heard
        %this.threatPosition = %obj.position; // Where we heard him
      }
    }
  }

When an NPC processes an object for foliage visibility, this code tells it to check to see if the object is close enough, if it's moving fast enough, and if a random condition is satisfied. If all is true, the threatType is set to "Player", and the threatPosition is set to where the player was "heard". This causes the react state to be triggered on the next think tick. When an NPC enters its react state with the threatType set to "Player", the NPC is programmed to move to the position, stop, and hunt for the threat before returning to his post.
#11
12/16/2009 (11:26 pm)
Arg... I was about to write 'sounds good' :P. It's so difficult to avoid awful puns when dealing with senses :P!
#12
12/16/2009 (11:56 pm)
I would say that's quite PUNny...Ha...ha ha. Funny joke. Laugh at the funny joke.
#13
12/17/2009 (10:25 am)
That was a pune - or, play on words!
#14
12/20/2009 (12:46 am)
One thing to keep in mind, when in a ghillie suit standing in front of a tree can hide you as well. You would want to account for different suits, snow, woodland ect as well.

I was playing COD MWF2 the other night as a sniper and people were running right by me and I was kneeling not prone. Knocked off 4 or 5 before they found me:) Of course that was humans....
#15
01/10/2010 (10:53 am)
Hey Bryce

This is truly great work. I just have one question. Will this be available for T3D?
#16
01/10/2010 (8:47 pm)
@Michael: It will be, I just develop with TGE because my computer can't handle T3D
#17
03/23/2010 (9:48 am)
Hey Bryce

Any word on the Tactical AI. Sure am looking at seeing this in action.
#18
03/23/2010 (1:57 pm)
Writing up documentation for it in preparation for release. I'm going to release the messy, hacky code that "just works" first, so that people can get set up with it, and I'll clean up the code and send out updates.