• entries
235
509
• views
172186

# State Lines

345 views

Over the last few days I have been re-factoring the AI code in Ancient Galaxy. It was written by another programmer, and was pretty good for the initial cut of it, but I didn't really take full ownership of it and re-think it later on, and now it's gotten out of control.

The original design had a Brain class, that contained a single current BrainState, which could be one of Waiting, Searching, Pathing, Wander, RePathing, Walking, Shooting, Hiding, etc.

This breaks down when you want the AI to be in more than one state at once, or remember what it was doing before, say Pathing. This was done in an ad-hoc manner of remembering a 'DestinationState' which was the state to do next. Part of the issue was that the DestinationState was what to do next if things went well. If you lost your target, couldn't make progress on your path, etc, it wasn't always clear what to do next.

I experimented with the idea of going for BehaviorTrees, which I have been using at work, but didn't want to implement a completely new system, and wanted the existing levels & save-games to work with any changes.

So, the current plan, which I have coded up, and am about to begin testing, is to have three concurrent states in the Brain class.

One Main State, which would be the AI's overall positioning & behavior strategy, like Flee, Wander, Follow, Patrol, None, etc.
One CombatState, which can be Shooting, Hiding ( for reloading, etc. ), Charging, None
One PathingState, which can be Pathing, RePathing, Searching or None.

The desired position-choosing logic used to be clustered in the Searching state, which used code like

if ( mDestinationState == "Charging" ) // choose a spot near the target

to choose a place with a good Line Of Fire, or a poor one ( for cover ), or near or far from the target, etc.

The new code uses virtual functions on the Brain states to get things like desired location bounding boxes, location evaluations, movement speeds, etc.

The Main State provides the bounding box of where to even look for navnode spots to go to. The Brain then chooses spots randomly inside the box. The Main State then gets to veto or evaluate these spots, then, if not vetoed, the Combat state can then chime in and veto or evaluate the remaining spots. Then the spot is selected randomly based on its desirability.

This way, the friendly NPCs can Follow the player, but still find Shooting and Hiding spots to attack enemy NPCs.

It feels great to rip out and simplify a bunch of code, but I'll let you know how it works out in game...

Hmm, interesting thing I stumbled into. I was looking around the Project Offset webpage (www.offsetsoftware.com) and was reading one of the developer blogs/interviews, and happened to remember reading our gdnet journal here earlier today... Do you happen to be working on this game with the graphics programmer over there? Or perhaps, this is you?

=)

EDIT: Just now noticed the interview was with SIM, not SLIM. So it is you. =)

Did you consider using a state stack?
This avoids the arbitrary 3 "types" of states (and the implied priority that comes along with that), and lets you smoothly mix task composition with interrupts.

Yes, I did think about this. The stack would work, as long as I could query states further down the stack for information and context.

I initially threw out the stack idea, because I was thinking that states couldn't know about their parents, and then realized that you don't have to do it that way! ;)