I’ve been getting my hands dirty again in the guts of our AI for a little project and it has got me thinking about refactoring AI Architecture. I’ve been pretty happy with my comprehension for the layers of the AI Architecture, from top to bottom.
I think we’ve got a pretty good generally consensus on most of these, but there’s that vague one in the middle that we don’t talk about much and it’s kinda a mess. That Anim-type chooser thingy. I’ve heard it called the Animation AI or just the “state machine” for those who’ve given up, but usually it just ends up being this vague code API that relies on some sort of horrifying combination of data-driven and hard-coded strings. If you’re lucky.
It should really be called something like the Anim Coordinator. An Anim Coordinator is the layer that takes your vague AI request and identifies the specifics needed for the Anim layers below. Back in the day, we thought this was pretty simple, but animations are getting pretty complex, and there’s a lot going on in there. For example, given the “go there” behavior, the Anim Coordinator identifies that the full body should then be walking, and serves as a central hub for fielding the related data requests: the destination from your Behavior, how much to turn from your Locomotion interface, the walk type from the game data, or whether you can even walk right now at all or have to wait for something, for example. The Behavior above doesn’t really care about these things. It wants to know when you’re done getting there, or maybe if you failed, but for the X seconds that it takes to negotiate the world, the upper part of the AI goes on holiday and can spend its CPU cycles getting drunk on jagermeister shots.
But just because the upper AI’s decided the best thing to do is go across a room doesn’t mean animation has enough information to pick some keyframes. There’s signals in and signals out that need to be processed, game state requests, conditionals, transitions between sequences of anims, etc. In particular, sequences of synchronized anims is tricky, like the Quick Time Events in God of War, and yet something that builders should be able to easily do frequently, and with substantial variation. (Do we really want the AI to have to have seperate Stun behavior for every sequence where the AI is stalling waiting for a button?)
So far, the best architecture I know of for this is the finite state machine. And there’s one big advantage to using a FSM – your player is probably already using one. Most games with lots of actions available to the player have a similar problem. Players hit buttons, which sends signals to animations to play. But not every animation can play after every button press, so there’s a whole FSM of valid animations from every point in time, game state interfaces and queries, conditions, etc. In my experience, works pretty well. Looks pretty much like what an FSM is supposed to look like.
There’s one problem – the Anim Coordinator doesn’t look like that.
In practice, most AI Behaviors want to be able to switch to every other behavior. Consider Behavior Trees, where there are traditionally no limitations on valid transitions, just pre-conditions in the Decision Making layer. If the Behavior says it should run, 9 times out of 10 the Anim Coordinator should try and comply. That means every macro Anim state in your FSM is connected to every other macro Anim state (assuming some sort of hierarchical organization for sanity). That’s the worst case for an FSM architecture imaginable. Oops. Some games have used planning to map through this morass, but that doesn’t make the FSM any easier to work with, it just means you can have longer independent state strings.
In the other Anim Coordinator architectures, the parts that seem to work well are these automatic transition-based approaches (insert the technical term here) – recognize the request to switch anim types, end the current type, and start the new one. Unfortunately, the UI for these is pretty horrible, and in an ideal world, an animator/designer can go into your Anim coordinator and add custom transition states and custom game state reactions. I’ve never once seen that happen. I heard of it happening one time, but apparently that animator was a whiz kid coder too who half-built the FSM, so I’m not sure that counts.
We need a UI that expresses this “send all” and “receive all” concept with good debugging to follow them, but still has all the functionality of the hFSM, for those cases where we really do need to force a certain kind of transition.
I want to see animators/designers sitting in front of these kinds of AI tools and using them. I think this time around I’d like to try a hFSM with these 2 special kinds of jump nodes, and make my initial version of the AI only use these nodes. Then allow the hFSM to use custom signals that different parts of the tree can set, to emulate a level of content scripting that the game state code doesn’t really need to know about right away. Like “my posture is sitting now”. It sounds simple. Hopefully, this time we’ll get it right.