private IEnumerator WaitForPreviousTransition(StateMapping i_NextState)
        {
            while (m_IsInTransition)
            {
                yield return(null);
            }

            ChangeState((T)i_NextState.state);
        }
예제 #2
0
        IEnumerator WaitForPreviousTransition(StateMapping nextState)
        {
            while (isInTransition)
            {
                yield return(null);
            }

            ChangeState((T)nextState.state);
        }
        private IEnumerator ChangeToNewStateRoutine(StateMapping i_NewState)
        {
            m_DestinationState = i_NewState;             // Chache this so that we can overwrite it and hijack a transition.

            if (m_CurrentState != null)
            {
                if (m_CurrentState.hasExitRoutine)
                {
                    m_ExitRoutine = m_CurrentState.ExitRoutine();

                    if (m_ExitRoutine != null)
                    {
                        yield return(m_StateMachineRunner.StartCoroutine(m_ExitRoutine));
                    }

                    m_ExitRoutine = null;
                }
                else
                {
                    m_CurrentState.ExitCall();
                }

                m_CurrentState.Finally();
            }

            m_PrevState    = m_CurrentState;
            m_CurrentState = m_DestinationState;

            if (m_CurrentState != null)
            {
                if (m_CurrentState.hasEnterRoutine)
                {
                    m_EnterRoutine = m_CurrentState.EnterRoutine();

                    if (m_EnterRoutine != null)
                    {
                        yield return(m_StateMachineRunner.StartCoroutine(m_EnterRoutine));
                    }

                    m_EnterRoutine = null;
                }
                else
                {
                    m_CurrentState.EnterCall();
                }

                // Broadcast change only after enter transition has begun.

                if (m_OnStateChanged != null)
                {
                    m_OnStateChanged((T)m_CurrentState.state);
                }
            }

            m_IsInTransition = false;
        }
예제 #4
0
        private IEnumerator ChangeToNewStateRoutine(StateMapping newState, StateTransition transition)
        {
            destinationState = newState;             //Chache this so that we can overwrite it and hijack a transition

            if (currentState != null)
            {
                if (currentState.hasExitRoutine)
                {
                    exitRoutine = currentState.ExitRoutine();

                    if (exitRoutine != null && transition != StateTransition.Overwrite)                     //Don't wait for exit if we are overwriting
                    {
                        yield return(engine.StartCoroutine(exitRoutine));
                    }

                    exitRoutine = null;
                }
                else
                {
                    currentState.ExitCall();
                }

                currentState.Finally();
            }

            lastState    = currentState;
            currentState = destinationState;

            if (currentState != null)
            {
                if (currentState.hasEnterRoutine)
                {
                    enterRoutine = currentState.EnterRoutine();

                    if (enterRoutine != null)
                    {
                        yield return(engine.StartCoroutine(enterRoutine));
                    }

                    enterRoutine = null;
                }
                else
                {
                    currentState.EnterCall();
                }

                //Broadcast change only after enter transition has begun.
                if (Changed != null)
                {
                    Changed((T)currentState.state);
                }
            }

            isInTransition = false;
        }
예제 #5
0
        public StateMachine(StateMachineRunner engine, MonoBehaviour component)
        {
            this.engine    = engine;
            this.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));
                stateLookup.Add(mapping.state, mapping);
            }

            //Reflect methods
            var methods = component.GetType().GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public |
                                                         BindingFlags.NonPublic);

            //Bind methods to states
            var separator = "_".ToCharArray();

            for (int i = 0; i < methods.Length; i++)
            {
                if (methods[i].GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Length != 0)
                {
                    continue;
                }

                var names = methods[i].Name.Split(separator);

                //Ignore functions without an underscore
                if (names.Length <= 1)
                {
                    continue;
                }

                Enum key;
                try
                {
                    key = (Enum)Enum.Parse(typeof(T), names[0]);
                }
                catch (ArgumentException)
                {
                    //Not an method as listed in the state enum
                    continue;
                }

                var targetState = stateLookup[key];

                switch (names[1])
                {
                case "Enter":
                    if (methods[i].ReturnType == typeof(IEnumerator))
                    {
                        targetState.hasEnterRoutine = true;
                        targetState.EnterRoutine    = CreateDelegate <Func <IEnumerator> >(methods[i], component);
                    }
                    else
                    {
                        targetState.hasEnterRoutine = false;
                        targetState.EnterCall       = CreateDelegate <Action>(methods[i], component);
                    }
                    break;

                case "Exit":
                    if (methods[i].ReturnType == typeof(IEnumerator))
                    {
                        targetState.hasExitRoutine = true;
                        targetState.ExitRoutine    = CreateDelegate <Func <IEnumerator> >(methods[i], component);
                    }
                    else
                    {
                        targetState.hasExitRoutine = false;
                        targetState.ExitCall       = CreateDelegate <Action>(methods[i], component);
                    }
                    break;

                case "Finally":
                    targetState.Finally = CreateDelegate <Action>(methods[i], component);
                    break;

                case "Update":
                    targetState.Update = CreateDelegate <Action>(methods[i], component);
                    break;

                case "LateUpdate":
                    targetState.LateUpdate = CreateDelegate <Action>(methods[i], component);
                    break;

                case "FixedUpdate":
                    targetState.FixedUpdate = CreateDelegate <Action>(methods[i], component);
                    break;

                case "OnCollisionEnter":
                    targetState.OnCollisionEnter = CreateDelegate <Action <Collision> >(methods[i], component);
                    break;
                }
            }

            //Create nil state mapping
            currentState = new StateMapping(null);
        }
예제 #6
0
        public void ChangeState(T newState, StateTransition transition)
        {
            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 == nextState)
            {
                return;
            }

            //Cancel any queued changes.
            if (queuedChange != null)
            {
                engine.StopCoroutine(queuedChange);
                queuedChange = null;
            }

            switch (transition)
            {
            //case StateMachineTransition.Blend:
            //Do nothing - allows the state transitions to overlap each other. This is a dumb idea, as previous state might trigger new changes.
            //A better way would be to start the two couroutines at the same time. IE don't wait for exit before starting start.
            //How does this work in terms of overwrite?
            //Is there a way to make this safe, I don't think so?
            //break;
            case StateTransition.Safe:
                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;
                    }
                }
                break;

            case StateTransition.Overwrite:
                if (currentTransition != null)
                {
                    engine.StopCoroutine(currentTransition);
                }
                if (exitRoutine != null)
                {
                    engine.StopCoroutine(exitRoutine);
                }
                if (enterRoutine != null)
                {
                    engine.StopCoroutine(enterRoutine);
                }

                //Note: if we are currently in an EnterRoutine and Exit is also a routine, this will be skipped in ChangeToNewStateRoutine()
                break;
            }


            if ((currentState != null && currentState.hasExitRoutine) || nextState.hasEnterRoutine)
            {
                isInTransition    = true;
                currentTransition = ChangeToNewStateRoutine(nextState, transition);
                engine.StartCoroutine(currentTransition);
            }
            else             //Same frame transition, no coroutines are present
            {
                if (currentState != null)
                {
                    currentState.ExitCall();
                    currentState.Finally();
                }

                lastState    = currentState;
                currentState = nextState;
                if (currentState != null)
                {
                    currentState.EnterCall();
                    if (Changed != null)
                    {
                        Changed((T)currentState.state);
                    }
                }
                isInTransition = false;
            }
        }
        private void ChangeToNewState(T i_NewState)
        {
            if (m_StateLookup == null)
            {
                throw new Exception("States have not been configured, please call initialized before trying to set state.");
            }

            if (!m_StateLookup.ContainsKey(i_NewState))
            {
                throw new Exception("No state with the name " + i_NewState.ToString() + " can be found. Please make sure you are called the correct type the statemachine was initialized with.");
            }

            StateMapping nextState = m_StateLookup[i_NewState];

            if (m_CurrentState == nextState)
            {
                return;
            }

            // Cancel any queued changes.

            if (m_QueuedChange != null)
            {
                m_StateMachineRunner.StopCoroutine(m_QueuedChange);
                m_QueuedChange = null;
            }

            if (m_IsInTransition)
            {
                if (m_ExitRoutine != null)          // We are already exiting current state on our way to our previous target state.
                {
                    m_DestinationState = nextState; // Overwrite with our new target.
                    return;
                }

                if (m_EnterRoutine != null) // We are already entering our previous target state. Need to wait for that to finish and call the exit routine.
                {
                    m_QueuedChange = WaitForPreviousTransition(nextState);
                    m_StateMachineRunner.StartCoroutine(m_QueuedChange);
                    return;
                }
            }

            if ((m_CurrentState != null && m_CurrentState.hasExitRoutine) || nextState.hasEnterRoutine)
            {
                m_IsInTransition = true;

                m_CurrentTransition = ChangeToNewStateRoutine(nextState);
                m_StateMachineRunner.StartCoroutine(m_CurrentTransition);
            }
            else // Same frame transition, no coroutines are present.
            {
                if (m_CurrentState != null)
                {
                    m_CurrentState.ExitCall();
                    m_CurrentState.Finally();
                }

                m_PrevState    = m_CurrentState;
                m_CurrentState = nextState;

                if (m_CurrentState != null)
                {
                    m_CurrentState.EnterCall();

                    if (m_OnStateChanged != null)
                    {
                        m_OnStateChanged((T)m_CurrentState.state);
                    }
                }

                m_IsInTransition = false;
            }
        }
        // CTOR

        public StateMachine(StateMachineRunner i_StateMachineRunner, MonoBehaviour i_Component)
        {
            m_StateMachineRunner = i_StateMachineRunner;

            m_Component = i_Component;

            // Define states.

            Array values = Enum.GetValues(typeof(T));

            if (values.Length < 1)
            {
                throw new ArgumentException("Enum provided to Initialize must have at least 1 visible definition.");
            }

            m_StateLookup = new Dictionary <object, StateMapping>();
            for (int valueIndex = 0; valueIndex < values.Length; ++valueIndex)
            {
                StateMapping mapping = new StateMapping((Enum)values.GetValue(valueIndex));
                m_StateLookup.Add(mapping.state, mapping);
            }

            // Reflect methods.

            MethodInfo[] methods = i_Component.GetType().GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic);

            // Bind methods to states.

            string separatorString = "_";

            char[] separator = separatorString.ToCharArray();

            for (int methodIndex = 0; methodIndex < methods.Length; ++methodIndex)
            {
                MethodInfo methodInfo = methods[methodIndex];

                if (methodInfo.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Length != 0)
                {
                    continue;
                }

                string methodName = methodInfo.Name;

                string[] nameSplit = methodName.Split(separator);

                // Ignore functions without an underscore.

                if (nameSplit.Length <= 1)
                {
                    continue;
                }

                Enum key;
                try
                {
                    key = (Enum)Enum.Parse(typeof(T), nameSplit[0]);
                }
                catch (ArgumentException)
                {
                    continue; // Selected method is not list in state enum.
                }

                StateMapping targetMapping = m_StateLookup[key];

                switch (nameSplit[1])
                {
                case "Enter":

                    if (methodInfo.ReturnType == typeof(IEnumerator))
                    {
                        targetMapping.hasEnterRoutine = true;
                        targetMapping.EnterRoutine    = CreateDelegate <Func <IEnumerator> >(methodInfo, i_Component);
                    }
                    else
                    {
                        targetMapping.hasEnterRoutine = false;
                        targetMapping.EnterCall       = CreateDelegate <Action>(methodInfo, i_Component);
                    }

                    break;

                case "Exit":

                    if (methodInfo.ReturnType == typeof(IEnumerator))
                    {
                        targetMapping.hasExitRoutine = true;
                        targetMapping.ExitRoutine    = CreateDelegate <Func <IEnumerator> >(methodInfo, i_Component);
                    }
                    else
                    {
                        targetMapping.hasExitRoutine = false;
                        targetMapping.ExitCall       = CreateDelegate <Action>(methodInfo, i_Component);
                    }

                    break;

                case "Finally":

                    targetMapping.Finally = CreateDelegate <Action>(methodInfo, i_Component);

                    break;

                case "FixedUpdate":

                    targetMapping.FixedUpdate = CreateDelegate <Action>(methodInfo, i_Component);

                    break;

                case "Update":

                    targetMapping.Update = CreateDelegate <Action>(methodInfo, i_Component);

                    break;

                case "LateUpdate":

                    targetMapping.LateUpdate = CreateDelegate <Action>(methodInfo, i_Component);

                    break;
                }
            }

            // Create nil state mapping.

            m_CurrentState = new StateMapping(null);
        }