When first starting out, there was a lot of features planned for my First Person Controller. This meant that a lot of code would probably affect the character. My experience with character controllers are that they get bloated very fast. Usually, most code ends up in the same file, with a lot of quick and dirty fixes everywhere. This was exactly what I wanted to avoid this time around, and this state machine was my solution.
This is a very simple state machine. It contains a collection of states and an enum defining the current state. I decided to avoid a property-based approach with dynamic conditions and transitions, and instead settle for a CheckConditions() function. This means that the conditions cannot change after compilation, and that the author has to manually specify conditions. However, this also means that the author has a lot more freedom when specifying these conditions and transitions.
The CheckConditions() and Update() functions in a State are identical, the only difference is when they are called. Update() is only called for the current state, while CheckConditions() is called for every state each frame. The return-value of these functions is the resulting state. Type::NONE means that the current state should not change.
The only remaining problem was how to deal with shared code between these states. I decided to introduce a helper-file, containing commonly used functionality like looking, moving and jumping.
The extra time spent on planning and implementing this system was something I did not regret. The states are modular and easy to toggle. Separating behaviour into independent states made the code naturally more structured. This made eveything much easier to visualize and debug while also keeping iteration times low.