Finite State Machines are series of design patterns often used in Game Development. In this article I’ll define what they are & how they work, go over 3 example implementations I have implemented in
Corona SDK, and describe some of the pain points I’ve encountered using them. I’ve ported Cassio Souza’s
ActionScript 3 State Machine to a
Lua State Machine and included examples. This article has a companion video embedded below.
What Are State Machines At their simplest form, State Machines are an
Observer pattern with 2 values they watch internally: an Entity and the Current State. Whenever someone, somewhere changes the internal state, it’ll let the world know. The high level parts are:
- States which contain/define the behavior for the Entity
- Transitions which occur when one State changes to another
- Rules which define which States can change to certain other States
- Events which are internally or externally dispatched which trigger State Transitions
When creating State Machines, you usually know all the states up front, hence the word finite. Whether a list on paper or a bunch of boxes on a flow chart with arrows denoting where you can go from where. Some states can have parent child relationships. There can be a variety of rules that allow/prevent some states from changing to others can causing state transitions.
Why Use State Machines There are 3 use cases State Machines. These are Artificial Intelligence, modeling simulations, and a refactoring path for game entities. For AI programming, you have the choice of deterministic or non-deterministic. Most video games have deterministic; meaning you know how the enemies will react based on different inputs. If they don’t see you, they’ll patrol. If they see you, they’ll attack you. If you hide and wait, they’ll eventually go back to patrolling. You can simulate fuzzy logic by using random numbers to change state to something random. For example, sometimes the enemy could use a grenade vs. a gun, or attack you immediately vs. sounding the alarm… or even run away to get reinforcements. Sometimes they could even talk to you instead. This randomness makes a State Machine non-deterministic, meaning you know all the States, but you don’t know exactly all the paths between States. For modeling simulations, whether complex machinery, or situations, you often have a ton of moving parts. You’re interested in the potential interactions when certain things are in certain states. Sometimes you want these situations to be repeatable. For example, for simulating hardware of large steel manufacturing machinery for training purposes, you want to setup the machine either in a specific state, or towards one to teach a new operator how to handle a certain negative situation in a safe environment to learn. You start a predefined set of actions to get the machine in a state where something bad is about to happen, and the operator has to learn how to push the right buttons or turn the right knobs to stop things from going bad. You can do the same at a macro level for emulating events. Think SimCity. This is also why unit testing deterministic state machines is pretty easy, albeit not as fun as non-deterministic, because you know all the paths ahead of time and the potential interactions. For the purpose of this article, we’re concerned with refactoring paths for game entities. Specifically, “dude, this code is too complex to manage and there are 50 billion boolean flags/if then statements to deal with…”. Using State Machines makes your code more manageable as it grows, more organized, and easier to debug.
Behavior in a Class vs. Behavior in States Alan Skorkin has
a great article on why developers often don’t use state machines. While he’s referring to software development as a whole, not just game development, he brings up a lot of valid points. Specifically,
YAGNI, or if you don’t know ahead of time you’ll have a lot of behavior, you start out building only what you need. The issue becomes down the line, the effort towards refactoring it to a proper state machine is a lot of work. I experienced this from 2 angles. The first is what got me interested in state machines in the first place. My game entities were getting uber complex, and I asked around for advice. Colleagues recommended Finite State Machines to solve all my problems. During the refactoring, I started to learn why they’re great for lots of behavior in known states. I then tried implementing one in a brand new code base with only a little behavior… and it was overly complex. Then I reached this mid-point where the complexity was hard to manage, but I didn’t want to go back to a State Machine again. So I’ve had a frustrating experience and an extremely positive one. You can see a
before and
after example of how State Machines can really make your game entity classes smaller and more manageable as their behavior grows.
Example 1: Simple State – Event Handler Let’s show a simple
1942 ‘esque example in Lua. You have an overhead view of an airplane that has 3 firing modes: a single bullet, faster dual-bullets, and a 3 pronged super-fast bullet mode[...]
Read more: Finite State Machines in Game Development
No comments:
Post a Comment