Stealth AI on a Budget

Enemy AI in stealth games can be tricky to get right. Enemies need to appear smart and responsive, yet be predictable enough that the player can form and execute a plan to avoid or escape them.

One of the best tricks to make enemy AI look smart is to make the character say something relevant to the situation. For example, if two agents engage in a brief call-and-response while searching for a target, many players will believe the AI is sharing knowledge between the two agents, regardless of whether it actually is.

In NEON STRUCT, I’m working without that particular secret weapon. For scope, budget, and tone reasons, I’ve chosen to forgo voice acting, so I’ve had to find other ways to indicate AI state. The quintessential example of a voiceless AI indicator is Metal Gear Solid‘s exclamation point, so I’m starting there. Enemies in NEON STRUCT have multiple awareness states, so each one has a different icon (and associated audio tone).

These states reflect the AI’s awareness of the player or other targets, and also correspond directly to the enemy’s behavior. The “…” means the AI has only limited awareness. It corresponds to the Notice behavior, which makes the enemy pause momentarily to look in the direction of the disturbance.

The “?” means the AI has increased awareness of the target, but not enough awareness to engage with a threat. For example, the AI may have heard loud footsteps or seen a dead body, but not yet found the actual target. This corresponds to the Search behavior, in which the enemy cautiously investigates the disturbance.

Finally, the “!” means the AI has enough visual awareness of the target to identify it as a threat. That corresponds to the Combat behavior, which is relatively simple because the player has no weapon of her own. Combative enemies chase the target to melee distance while firing a ranged weapon if available.

Awareness is a complex system that combines multiple fuzzy inputs (sight and hearing at various distances, illumination levels, etc.) into a discrete number of states. Determining the appropriate thresholds at which to switch between states is part of the secret sauce of stealth AI, and one of the factors which will be undergoing continuous evaluation and iteration during playtesting.

On the Level

I’ve spent the last couple of weeks blocking out two new levels (called “Streets3” and “HighRise1”) and improving two older ones (“SafeHouse” and “Hospital”).

HighRise1 takes place about halfway through the game, and is mostly comprised of conversations. Writing dialogue has been a new challenge for me in NEON STRUCT, and I’m not entirely confident in what I’ve written so far; but I tend to err on the side of short conversations to keep things moving.

Streets3 has been one of my favorite levels to build so far. I’ve wanted to put a neon-lit 1980s-esque mall in a video game for a while, and I finally had a place for it in this game. I spent one morning last week looking up reference photos of dead malls, which was rather depressing despite my interest in the subject.

Streets3 is a social level the first time the player visits it, but will be repurposed as a stealth level on a return trip. I had originally planned for it to be a hub which the player would revisit several times during the middle part of the game, but streamlining the level flow negated the need for a hub. Building a level to serve as both a social and stealth space has been an interesting challenge. The social levels in NEON STRUCT tend to contain very open spaces, and Streets3 is no exception, so stealth play will necessarily be based more on shadows and enemy facing than occlusion.

I returned to the SafeHouse level to add some extra decoration and finalize the quests. I’m finding it to be more fruitful to bounce between levels than to try to complete each level in a week. It gives me a broader range of tasks to pick from on any given day, and affords a lot more time to reflect on the levels before continuing to work on them.

For example, the first pass of the Hospital was an oversized monolith of a level, built without a clear purpose for many rooms and too much concern for realism in its floor plan. I revisited it this week, hacked off a third of it, and redirected the flow to fit the tighter space. The result is a less realistic but far more interesting map, with fewer prefab rooms and more identifiable landmarks.

Finally, I threw together a training level for upcoming playtests. I don’t expect to ship this—tutorialization in the final game will be done organically in the first real level—but I needed a way to start play testers from any level of the game without confusion about the mechanics.

Whether I ship this level or not, the System Shock 2-inspired neon cube world (which I’m tentatively calling “the Struct” to retroactively justify the game’s title) will definitely be making another appearance somewhere else in the game.

Little Computer People

I’ve spent most of the last week revisiting the levels from the vertical slice milestone, rebuilding bits and polishing up things that got missed the first time around. But that’s not super interesting to write about, so I won’t.

I also finally implemented a skin tone randomizer which had been on my task list for months. I kept procrastinating because I wasn’t sure how I wanted to implement it. I was considering making a 1D skin tone gradient texture and sampling randomly from it, but first I decided to just try it using the same random color generator code I had already written for outfit randomization. The results (shown above) are surprisingly usable. There’s a few odd-looking outliers, but I think it’s good enough to keep.

The trick I use to randomly generate colors is to use HSV space instead of RGB. Randomly generated colors in RGB space tend to have a broader range of hues than is desired. By using HSV space and constraining the hue to a narrow band, I can produce a plausible range of skin tones by varying the saturation and value.

For reference, the ranges I’m using are:
H: 14.4° – 21.6°
S: 20% – 70%
V: 40% – 100%

Another long neglected task I finally got done this week was a proper treatment for conversation portraits. My original plan was to have stylized illustrations in glowing neon colors, probably traced over stock photo portraits; but I wasn’t satisfied with the effect. Recently, I had the idea to render the characters as neon signs. The line art by itself is unremarkable, but bumping up its value and applying the usual post effects chain (bloom, chromatic aberration, film grain, and color grading) makes it pop like a neon sign should.