예제 #1
0
        IEnumerator WaitForPreviousTransition(StateMapping nextState)
        {
            while (isInTransition)
            {
                yield return(null);
            }

            ChangeState(nextState.state);
        }
예제 #2
0
        private IEnumerator ChangeToNewStateRoutine(StateMapping newState)
        {
            destinationState = newState; //Chache this so that we can overwrite it and hijack a transition

            if (currentState != null)
            {
                exitRoutine = currentState.Exit();

                if (exitRoutine != null)
                {
                    yield return(StartCoroutine(exitRoutine));
                }

                exitRoutine = null;

                currentState.Finally();
            }

            currentState = destinationState;

            if (currentState != null)
            {
                enterRoutine = currentState.Enter();

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

                enterRoutine = null;

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

            isInTransition = false;
        }
예제 #3
0
        public void Initialize <T>(StateBehaviour entity)
        {
            //Define States
            var values = Enum.GetValues(typeof(T));

            stateLookup = new Dictionary <Enum, 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 = entity.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++)
            {
                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)
                {
                    //Some things (evetns, properties) generate automatic method. Ignore these
                    for (int j = 0; j < ignoredNames.Length; j++)
                    {
                        if (names[0] == ignoredNames[j])
                        {
                            goto SkipWarning;
                        }
                    }

                    Debug.LogWarning("Method with name " + methods[i].Name + " could not resolve a matching state. Check method spelling");
                    continue;

SkipWarning:
                    continue;
                }

                var targetState = stateLookup[key];

                switch (names[1])
                {
                case "Enter":
                    if (methods[i].ReturnType == typeof(IEnumerator))
                    {
                        targetState.Enter = CreateDelegate <Func <IEnumerator> >(methods[i], entity);
                    }
                    else
                    {
                        var action = CreateDelegate <Action>(methods[i], entity);
                        targetState.Enter = () => { action(); return(null); };
                    }
                    break;

                case "Exit":
                    if (methods[i].ReturnType == typeof(IEnumerator))
                    {
                        targetState.Exit = CreateDelegate <Func <IEnumerator> >(methods[i], entity);
                    }
                    else
                    {
                        var action = CreateDelegate <Action>(methods[i], entity);
                        targetState.Exit = () => { action(); return(null); };
                    }
                    break;

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

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

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

                case "FixedUpdate":
                    targetState.FixedUpdate = CreateDelegate <Action>(methods[i], entity);
                    break;
                }
            }
        }
예제 #4
0
        public void ChangeState(Enum newState, StateTransition transition = StateTransition.Safe)
        {
            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)
            {
                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);
                        StartCoroutine(queuedChange);
                        return;
                    }
                }
                break;

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

                if (currentState != null)
                {
                    currentState.Finally();
                }

                currentState = null;     //We need to set current state to null so that we don't trigger it's exit routine
                break;
            }

            isInTransition    = true;
            currentTransition = ChangeToNewStateRoutine(nextState);
            StartCoroutine(currentTransition);
        }