What I did here, that I thought worked out pretty well, was to have a generic FSM class, that manages two things: States and Events.
States are, well, states… They have callbacks for update, fixedUpdate, lateUpdate, and for Enter and Leave events. Those all come in from the FSM class.
Events are objects that define transitions between states. They also have callbacks, one that returns a bool for evaluating a condition (which causes the event transition to happen), and another for when the event fires. Events are added to states (through the FSM), and can be added to multiple states, or to all states excluding a few given ones (very useful this).
So, basically it works on a simple loop, that just calls the updates on the current state, and for each event assigned to it, calls the updates on that too, and runs each event’s condition evaluation. If that evaluation passes, it switches to the next state (defined on the Event), calling the Leave and Enter methods on the appropriate states.
What makes this very handy, is that because all states are just collections of callbacks, you can assign methods that do things to each state, and multiple states can share the same methods.
For instance, if I have a method that controls character movement on the ground, I can assign that method to any state in which the character is movable on the ground. Similarly, if I have a ‘jump’ event, I can assign that event to multiple states, like idling on the ground, walking, and running. That way, you don’t repeat any code at all. You just assign the same methods to multiple states and events, however you want.
It takes some thinking to organize it all before coding, but then again, all FSMs do.
Cheers