private IEnumerator WaitForPreviousTransition(StateMapping nextState) { while (_isInTransition) { yield return(null); } ChangeState((T)nextState.State); }
private IEnumerator ChangeToNewStateRoutine(StateMapping newState) { _destinationState = newState; //Chache this so that we can overwrite it and hijack a transition if (CurrentStateMap != null) { if (CurrentStateMap.HasExitRoutine) { _exitRoutine = CurrentStateMap.ExitRoutine(); if (_exitRoutine != null) //Don't wait for exit if we are overwriting { yield return(_engine.StartCoroutine(_exitRoutine)); } _exitRoutine = null; } else { CurrentStateMap.ExitCall(); } CurrentStateMap.Finally(); } _lastState = CurrentStateMap; CurrentStateMap = _destinationState; if (CurrentStateMap != null) { if (CurrentStateMap.HasEnterRoutine) { _enterRoutine = CurrentStateMap.EnterRoutine(); if (_enterRoutine != null) { yield return(_engine.StartCoroutine(_enterRoutine)); } _enterRoutine = null; } else { CurrentStateMap.EnterCall(); } //Broadcast change only after enter transition has begun. if (Changed != null) { Changed((T)CurrentStateMap.State); } } _isInTransition = false; }
public StateMachine(StateMachineRunner engine, MonoBehaviour component) { _engine = engine; Component = component; var values = Enum.GetValues(typeof(T)); if (values.Length < 1) { throw new ArgumentException("Enum provided to Initialize must have at least 1 visible definition"); } _stateLookup = new Dictionary <object, StateMapping>(); for (var i = 0; i < values.Length; i++) { var mapping = new StateMapping((Enum)values.GetValue(i)); _stateLookup.Add(mapping.State, mapping); } var classMethods = component.GetType() .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic); var baseMethods = component.GetType() .BaseType.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic); List <MethodInfo> methodArray = new List <MethodInfo>(); methodArray.AddRange(classMethods); methodArray.AddRange(baseMethods); for (var i = 0; i < methodArray.Count; i++) { var attributes = (FSMAttribute[])methodArray[i].GetCustomAttributes(typeof(FSMAttribute), false); if (attributes.Length == 1) { var attribute = attributes[0]; Enum key; try { key = (Enum)Enum.Parse(typeof(T), attribute.EnumValue); } catch (ArgumentException) { //Not an method as listed in the state enum continue; } var targetState = _stateLookup[key]; switch (attribute.ActionName) { case FSMActionName.Enter: if (methodArray[i].ReturnType == typeof(IEnumerator)) { targetState.HasEnterRoutine = true; targetState.EnterRoutine = CreateDelegate <Func <IEnumerator> >(methodArray[i], component); } else { targetState.HasEnterRoutine = false; targetState.EnterCall = CreateDelegate <Action>(methodArray[i], component); } break; case FSMActionName.Exit: if (methodArray[i].ReturnType == typeof(IEnumerator)) { targetState.HasExitRoutine = true; targetState.ExitRoutine = CreateDelegate <Func <IEnumerator> >(methodArray[i], component); } else { targetState.HasExitRoutine = false; targetState.ExitCall = CreateDelegate <Action>(methodArray[i], component); } break; case FSMActionName.Finally: targetState.Finally = CreateDelegate <Action>(methodArray[i], component); break; case FSMActionName.Update: targetState.Update = CreateDelegate <Action>(methodArray[i], component); break; } } } CurrentStateMap = new StateMapping(null); }
public void ChangeState(T newState) { if (_stateLookup == null) { throw new Exception( "States have not been configured, please call initialized before trying to set state"); } if (!_stateLookup.ContainsKey(newState)) { throw new Exception("No state with the name " + newState + " can be found. Please make sure you are called the correct type the statemachine was initialized with"); } var nextState = _stateLookup[newState]; if (CurrentStateMap == nextState) { return; } //Cancel any queued changes. if (_queuedChange != null) { _engine.StopCoroutine(_queuedChange); _queuedChange = null; } if (_isInTransition) { if (_exitRoutine != null) //We are already exiting current state on our way to our previous target state { //Overwrite with our new target _destinationState = nextState; return; } if (_enterRoutine != null) //We are already entering our previous target state. Need to wait for that to finish and call the exit routine. { //Damn, I need to test this hard _queuedChange = WaitForPreviousTransition(nextState); _engine.StartCoroutine(_queuedChange); return; } } if ((CurrentStateMap != null && CurrentStateMap.HasExitRoutine) || nextState.HasEnterRoutine) { _isInTransition = true; _currentTransition = ChangeToNewStateRoutine(nextState); _engine.StartCoroutine(_currentTransition); } else { if (CurrentStateMap != null) { CurrentStateMap.ExitCall(); CurrentStateMap.Finally(); } _lastState = CurrentStateMap; CurrentStateMap = nextState; if (CurrentStateMap != null) { CurrentStateMap.EnterCall(); if (Changed != null) { Changed((T)CurrentStateMap.State); } } _isInTransition = false; } }