Feel free to review the glossary of terms used within this tutorial. It is best that you know what I am talking about before or while you read the content.
Aside from controlling NPCs, bots, dialog, and environmental conditions within video games, FSMs also have a large role outside of the video game industry. For example, cars, airplanes, and robotics (machinery) have complex FSMs. You could even say websites have a FSM. Websites that offer menus for you to traverse other detailed sections of the website act much like a FSM does with transitions between states.
You are in the kitchen (state) and you felt a sudden urge to drink water. You then decide to walk over to the cupboard and grab a glass. This is an event (walk to cupboard) and you are now in a new state with new options available to you. You then open the cupboard (action) and grab a glass (action). With the glass, you walk over to the sink (event) and turn on the tap (action). Water into poured into the glass (event) and you then drink it (action).
So in a nutshell, an event leads you to a state in which you perform an action or several actions (though you do not always have to perform an action). So when you walk to the cupboard, that leads you to the state "At Cupboard" where the typical action is "Open Cupboard". To better demonstrate a FSM, a robot example will be used throughout this tutorial.
Event | State |
turnOn | Activated |
turnOff | Deactivated (Idle) |
stop | Stopped |
walk | Walking |
run | Running |
raiseLeftArm | LeftArmRaised |
lowerLeftArm | LeftArmLowered |
lowerLeftArm | LeftArmLowered |
raiseRightArm | RightArmRaised |
lowerRightArm | RightArmLowered |
turnHead | HeadTurned(direction) |
speak | Talking(text) |
When developing FSMs, you should expect to see an even greater list of events and states to support. As you will see later, as logic errors are common in programming, event errors are common in FSMs.
Just before we move on, I would like to make a few notes about the selected states above. States do not have multiple roles. Every time Bender is turned on, the same flow of direction will always occur. If at any time in your projects there is a breech in that flow, it would need to be fixed. This is one of the problems with FSMs because sooner or later someone is going to pick up the pattern. Ways around this would be to add more events to a given situation or randomize the actions performed.
Secondly, states are unique. No two states should have the same reaction to a specified event. For example, having Bender "speak" and "shout" are synonymous in terms of speech; therefore we eliminate one while keeping the other and supplement arguments to dictate how a certain text is pronounced (be it calm, yelling, stutter, etc…).
Thirdly, state transitions do not normally branch. By branch I mean through the use of conditional statements. For example, Bender has a set of movement options. Such movements can be decided via IF/THEN statements, however it is best to avoid that since the purpose behind states is to replace the need for IF/THEN statements with events. Although states are flexible and do allow such conditional operations, it is always best to avoid them. It reduces complexity and lessens the chance for logic errors.
Now that we have a list of events and states, it is best to draw them out. When you visually see your model, it is easier to pick out where things may go wrong or where things could use improvements.
Now that we have a state diagram, we need to examine it further and construct a state table. A state table is nothing more than a table listing all the states, the actions to get to them, and the reaction performed by them. For example, when Bender turns on, it puts itself in the turned on state. From here, it is allowed to conduct various movements and shut itself down. When in the activity state, it cannot directly shut itself down. So the state table illustrates to us what and when can Bender perform certain actions.
# | Event | Actions | Comment | Caused By | Will Effect |
1 | turnOn | bootUp | Bender is turning on | 2-10 | |
2 | bootUp | Activity | Allow various activities | 1 | 3-8 |
3 | walk | goWalk | Bender will begin to walk | ||
4 | run | goRun | Bender will begin to run | ||
5 | talk | say(text) | Bender will say "text" | ||
6 | turnHead | turningHead | Bender rotates head | ||
7 | raiseArm | raisingArm | Bender raises arm (left or right) | ||
8 | lowerArm | laweringArm | Bender lowers arm (left or right) | ||
9 | stop | powerDown | Bender stops current action | 3-8 | |
10 | turnOff | shutDown | Bender will shut off | 1-9 |
Putting it all together, the state diagram helps illustrate what the functionality is like and the state table defines the types of inputs to those functions and the expected outputs. So, with a complete state diagram and state table, you have the blueprints to develop your FSM.
How The Code Works:
There are two major classes.
1) State: | The state class is responsible for setting transitions, specifying actions, and pointing to the right function that represents the action. In this code, a state supports the 3 preliminary specifications: OnEntry, Do, and OnExit, and supports an unlimited amount of OnEvent specifications. These specifications are used to define your actions. This should help you practice with designing much larger FSMs. |
2) FSM: | The FSM class is a collection of states. Its sole purpose is to function as a controller between states and execute the necessary state actions based on the events passed to it. |
Also, I have not placed any fancy reactions in the code (such as guard conditions). I simply want to demonstrate how state transitions occur and provide a clean and elegant interface to handling FSMs. Feel free to add new features and reactions to them.
1) Review the problem. Write down all the entities involved
2) Design a state diagram from the work you did in 1)
3) Review the state diagram. Make sure it meets all the requirements and does not fail under simple transitions.
4) Develop a state table to clarify the state diagram and correlate to the code.
5) Design the code.
1) FSM | A collection of states and transitions that outline a path of actions that may occur. |
2) State | A state is a position in time. For example, when you are at the bus stop, you are currently in a waiting state. |
3) Event | An event is something that happens in time. For example, the bus has arrived. |
4) Action | A task performed given a certain event that occurred. For example, you enter the bus. |
5) Transition | A link between 2 states. May be unidirectional or bidirectional. |