Friday, July 11, 2008

Game State Management : Finite State Machines

I've added a subsequent post on state management in game menus. My previous post on game state covered high-level game state management. I next tried writing this post, but felt that covering feature creep was first up. I'll explain that first.

The whole trick to writing a game state management system is figuring out where you need flexibility. A system that's too complex winds up being buggy and a pain to use. Stable, easy-to-use systems generally allow you to add more richness to a project, say by extending the system, and that's something that doesn't happen when you have buggy or painful code. Simple systems lead to power.

Game state systems are used to implement game AI or mechanics. For example, a building in an RTS might start in a Construction state, then transition into an Idle state, then move into a Production state then back to Idle. A Creature AI might use states like Patrol, Guard, Hunt, Attack, Move, or Sleep. Programmers usually implement these systems using finite state machines, and that's what I'll be talking about here.

Finite State Machines (FSMs) are easy to code. The major element is the State object; everything else is optional. That else includes Transitions & Transition Conditions, Events, and Actions.

So what's a state? Take the RTS example above. When a building is in the Construction state, it might cycle through several different stages, ie have several different images (for a 2D game) or models (for a 3D game) that represents that partial construction. Maybe the phase is one long animation; maybe it's a set of several different animations. Even for a 2D, non-animated game, you might display a progress bar or particle effect or something else indicating construction progress. Is each 'step' along that progress a different State?

And that's the decision to make. Why are you storing the state? I mentioned several different states for a building in an RTS, and these suggest the state that the building is considered to be in for the purposes of gameplay, not for animation. It doesn't matter how far along construction is, the player only has two choices: either leave it alone to continue construction, or cancel the construction. This is a good level to model the state. Leave the problem of animating the building to some other system; a useful state management system in this game will deal with "Under Construction" as one entire state. A building might have four states: Construction, Idle, Production, and Under Attack (a building under attack can't be repaired or start production).

Do you need a class to store this state? Another option is to store the state as an enum. Every game tick, you tell the building to draw itself, and it can switch depending on that enum, just as easily as looking at a state class. So why would you have a State class?

One reason would be to implement the Strategy pattern. The building object itself could hold a State object, where State is an abstract base class. Subclasses would implement virtual functions, which could be Events (User_Requested_Production, User_Cancels_Construction, etc) or queries (Can_Start_Production) or maybe even a Tick or Render function. This means creating new subclasses not just for each state that buildings could share (like Idle) but also per building and per game object. The question is, what behavior do different buildings share in their Idle states, and would different game objects (like Buildings and Infantry and Missiles) all have Production or Under Attack states? What's the shared code?

This is putting the cart before the horse. Don't be led into thinking that you need to implement a Finite State Machine class because lots of games use finite state machines. I recommend implementing (say) one game object (like Buildings) first, and one Building before the rest, and then see where you can pull common code out. Implement some simple method using a bunch of switch statements and enums and see, for your game genre and design, where you need flexibility. When you're writing the code for a particular building, you might wind up writing a bunch of switch statements, and when you see yourself doing that, that is a good time to decide to implement some kind of FSM abstraction into the code.

Do all game objects render themselves? Do all game objects share a common base class (maybe they don't!)? Maybe there's common code to render a game object. Maybe all Buildings share rendering code among themselves that isn't used by other game objects. Most likely, you're not going to want custom Render methods for each individual building, or worse, each state that each building can be in.

Let's go back to the Building interface. Other gameplay objects don't really care how buildings render themselves, or really even what state the building is in. They have specific questions: can you start production right now? Should I be displaying a Cancel button? Is the building damaged? There's lots of other state I'm skipping over, too, like how many hit points the building has. I reckon there's some OO nutball out there that thinks that hit points should be encapsulated into a state object, but you and me can laugh at him. My god, man, no way. Hit points are 'state', but there's no reason to have a state object there. An int is fine. Hit points are such a primary concept in a game like an RTS that your base Game Object (or Building) class should have an accessor for that value.

So the external interface that a Building object exposes doesn't contain any State-like abstract object. A State object might be useful for a Building to manage itself internally, but it's not something it tells its friends about.

The place to put flexibility in your code is where there's a lot of traffic. Buildings don't get a lot of traffic. There's not much that can be done to them -- they get attacked, they get repaired -- but they don't usually observe their environment. Buildings don't behave different when their hitpoints get low, or when an enemy comes into range, or if they see a friendly unit nearby. Maybe yours do, but at least they're far more "stable" when compared to other units (like infantry). A complex state management system with transitions and events getting fired and scriptable transition conditionals and substates and enter actions and exit actions and all that... not that useful for game objects that don't do a lot of those things.

Mobile AI units, like infantry in an RTS or mobs in a real-time RPG or enemies in a FPS, will do a lot of those things. A fancy state management system makes more sense for those kinds of AI units, and I'll cover them in more depth next time.

No comments: