I often use StateKit (a simple, object-based finite state machine implementation) for all kinds of state management. It gets used for enemy AI, player controllers, menus and just about everywhere else. In a recent project that was not very well defined (to be honest, it was made almsot completely on the fly) I needed a super flexible way to deal with a hierarchial state machine. The solution presented in this post is what I ended up using and it has been dubbed the PropEnumEvent system.
Let’s say you have a game with a main menu and some submenus. Maybe you have no idea how many menus and you need a nice, flexible way to deal with them. We’ll start things off by creating an enum that has a value for each menu/state in the game.
1 2 3 4 5 6 7
To add a bit of spice to the discussion, lets add some substates for the GameState.Map value:
1 2 3 4 5 6 7 8 9
If we were to whip up a quick diagram for what we have so far it might look something like this:
We can only be on one screen at any given time and the Map screen has several different sub states. Nothing groundbreaking here so far. We want to use these enums to drive the entire state of the game from launch until completion. We are going to need to show/hide UI, HUD elements, control enemy AI state etc. There is no limit to how far you can take it.
I personally hate tightly coupled code. It makes changing things, adding features and refactoring a huge PITA. MessageKit solves a lot of the tight coupling problems and it will be used for the PropEnumEvent system since I always already have it in whatever project I am working on. The meat of the solution is incredibly simple but quite powerful (and quite similar to INotifyPropertyChanged or KVO). All we are going to do is wire up a property that when changed uses MessageKit to post a message with the new enum value (note that plain old .NET events would work fine as well for this if you don’t use MessageKit). You’ll see in the code below the message is posted before the value is set. The reason for that is that sometimes the system listening for the message needs to know the previous state (UI for example so that it can do proper transitions). When the message is received the receiver will get the new state and they can access the old state via the state property.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
That’s it. Nothing more to it. In that little snippet of code we end up with a powerful, decoupled system to control our games. Adding new enum values as the game grows in complexity is no problem. Code can set the enum and trigger the message at any time easily. Some example message listeners from the project this was used in are HUD (activates/deactivates features based on state), canvas (hides/shows menus with animations based on state), player (enables/disables self, jumps to spawn point, increments death count).