Example #1
0
        private IEnumerator WaitForPreviousTransition(StateMapping nextState)
        {
            while (_isInTransition)
            {
                yield return(null);
            }

            ChangeState((T)nextState.State);
        }
Example #2
0
        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;
        }
Example #3
0
        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);
        }
Example #4
0
        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;
            }
        }