Little update! I changed my state machine again, so it uses delegates to Coroutines of each state handling, as I had mentioned before. It looks better now, I think I can keep this style for the remaining states :) Now I have something like:
private delegate IEnumerator individualStateHandler(); private Dictionary<characterStates, individualStateHandler> stateHandlers; void Awake () { changeState(characterStates.idle); stateHandlers = new Dictionary<characterStates, individualStateHandler>(); stateHandlers.Add(characterStates.idle, processIdleTransition); stateHandlers.Add(characterStates.running , processRunningTransition); stateHandlers.Add(characterStates.sprinting, processSprintingTransition); stateHandlers.Add(characterStates.jumping, processJumpingTransition); stateHandlers.Add(characterStates.falling, processFallingTransition); stateHandlers.Add(characterStates.walling, processWallingTransition); stateHandlers.Add(characterStates.wallJumping, processWallJumpingTransition); stateHandlers.Add(characterStates.lookingUpIdle, processLookingUpIdleTransition); } void Start() { StartCoroutine(stateHandlers[currentState]()); }
Where, my idle Coroutine, for example, consists of:
IEnumerator processIdleTransition() { playIfNotPlaying("Idle"); do { if (checkFallAndChangeState()) break; if (checkJumpAndChangeState()) break; if (rightKeptPressed || leftKeptPressed) { if (sprintKeptPressed ) { changeState(characterStates.sprinting); } else { changeState(characterStates.running); } break; } if (checkLookingUpAndChangeState()) break; yield return new WaitForEndOfFrame(); } while (currentState == characterStates.idle); yield return new WaitForEndOfFrame(); StartCoroutine(stateHandlers[currentState]()); }
That way, I "Start" my state machine operation on the Start() function, and after that, I always have one Coroutine corresponding to the current state running. If the current state changes, the Coroutine of the old state stops running and I start another Coroutine for the new state.
I specially liked this new approach new approach because it also makes it easy to insert code that should be run before or after the state. For instance, before the main loop of the idle state, I easily changed the animation being played to "Idle", and after that I keep the loop that treats that state, without the need to verify the current animation at each update, if it matches the current state, that way I removed my original updateAnimation() function from the main loop too.