/// <summary> /// Constructor /// </summary> /// <param name="component"> the subscriber </param> /// <exception cref="ArgumentException"> Enum provided to Initialize must have at least 1 visible definition </exception> public StateMachine(MonoBehaviour component) { //to track what is running state _stateStream.Subscribe(st => { //StateStream.OnNext(st.GetState()); if (Changed != null) { Changed.Invoke(st.GetState()); } if (_tempStates.Count < 1) { _currentState = st; _tempStates.Enqueue(_currentState); } else { _lastState = _tempStates.Dequeue(); _currentState = st; _tempStates.Enqueue(_currentState); } }).AddTo(component); _component = component; //Define States 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 (int i = 0; i < values.Length; i++) { var mapping = new StateMapping((Enum)values.GetValue(i), _stateStream); _stateLookup.Add(mapping.GetState(), mapping); } AddSubscriber_Internal(component); _initNotifyer.OnNext(Unit.Default); _initNotifyer.OnCompleted(); }
private void ChangeStateInternal(T newState, StateTransition transition, float enterDuration, float exitDuration) { 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.ToString() + " can be found. Please make sure you are called the correct type the statemachine was initialized with"); } var nextState = _stateLookup[newState]; if (_currentState != null && _currentState == nextState) { return; } //if (_StateInQeue != null && _StateInQeue == nextState) return; if (transition.Equals(StateTransition.Safe)) { if (_currentState == null) //for initial run { _queHistory.Add(nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; return; } if (_currentState.ExitInQue) //if current one has exit routine waiting { _queHistory.Add(_currentState.CreateNewRoutine(_currentState.HasExitRoutine ? exitDuration : 0f, false)); _queHistory.Add(nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; } else //already in current exit routine { if (!_StateInQeue.GetState().Equals(nextState.GetState())) //if next state is not in qeue { _queHistory.Add(nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; } } } else if (transition.Equals(StateTransition.Overwrite)) { if (_currentState == null) //for initial run { _queHistory.Add(nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; } else { //todo test more on OnCancle.Invoke //_StateInQeue.EnterCancel.Invoke(); _queHistory.Cancle(); _currentState.Finally.Invoke(); _queHistory.Add(nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; } } else //blend { if (_currentState == null) //for initial run { _queHistory.Add(nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; return; } if (_currentState.ExitInQue) //if current one has exit routine, waiting { _queHistory.AddPair(_currentState.CreateNewRoutine(_currentState.HasExitRoutine ? exitDuration : 0f, false), nextState.CreateNewRoutine(nextState.HasEnterRoutine ? enterDuration : 0f, true)); _StateInQeue = nextState; } } }