Пример #1
0
    private IEnumerator ChangeToNewStateRoutine(StateMapping newState)
    {
        isInTransition = true;

        foreach (StateMapping currentState in currentStates)
        {
            if (currentState != null)
            {
                var exitRoutine = currentState.Exit();

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

        currentStates = new List <StateMapping>();
        currentStates.Add(newState);

        if (newState != null)
        {
            var enterRoutine = newState.Enter();

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

        isInTransition = false;
    }
Пример #2
0
    private IEnumerator WaitForPreviousTransition(StateMapping nextState)
    {
        while (_isInTransition)
        {
            yield return(null);
        }

        ChangeState(nextState.State);
    }
Пример #3
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;
    }
Пример #4
0
    private IEnumerator ChangeToNewStateRoutine(StateMapping newState, StateTransition transtiion)
    {
        _destinationState = newState;

        if (_currentState != null)
        {
            if (_currentState.hasExitRoutine)
            {
                _exitRoutine = _currentState.ExitRoutine();

                if (_exitRoutine != null && transtiion != StateTransition.Overwrite)
                {
                    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();
            }

            if (Changed != null)
            {
                Changed(_currentState.State);
            }
        }

        _isInTransition = false;
    }
Пример #5
0
    private IEnumerator RemoveConcurrentStateRoutine(StateMapping newState)
    {
        currentStates.Remove(newState);
        if (newState != null)
        {
            var exitRoutine = newState.Exit();

            if (exitRoutine != null)
            {
                yield return(StartCoroutine(exitRoutine));
            }
        }
    }
Пример #6
0
    private IEnumerator AddConcurrentStateRoutine(StateMapping newState)
    {
        currentStates.Add(newState);

        if (newState != null)
        {
            var enterRoutine = newState.Enter();

            if (enterRoutine != null)
            {
                yield return(StartCoroutine(enterRoutine));
            }
        }
    }
Пример #7
0
    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;
    }
Пример #8
0
    public void Initialize <T>(MonoBehaviour 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 "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;
            }
        }
    }
Пример #9
0
    public StateMachine(StateMachineRunner engine, BaseState stateScript, string[] states)
    {
        _engine      = engine;
        _stateScript = stateScript;

        _stateLookup = new Dictionary <string, StateMapping> ();
        foreach (var state in states)
        {
            var mapping = new StateMapping(state);
            _stateLookup.Add(state, mapping);
        }

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

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

            int lastIdx = methods[i].Name.LastIndexOf('_');
            if (lastIdx < 0)
            {
                continue;
            }

            string name = methods[i].Name.Substring(0, lastIdx);
            if (!states.Contains(name))
            {
                continue;
            }

            StateMapping targetState = _stateLookup[name];

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

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

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

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

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

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

        _currentState = new StateMapping(null);
    }
Пример #10
0
    public void ChangeState(string 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");
        }

        StateMapping nextState = _stateLookup[newState];

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

        switch (transition)
        {
        case StateTransition.Safe:
            if (_isInTransition)
            {
                //Already exiting current state on our way to our previous target state
                if (_exitRoutine != null)
                {
                    _destinationState = nextState;
                    return;
                }

                if (_enterRoutine != null)
                {
                    _queuedChange = WaitForPreviousTransition(nextState);
                    _engine.StartCoroutine(_queuedChange);
                    return;
                }
            }
            break;

        case StateTransition.Overwrite:
            if (_currentTransition != null)
            {
                _engine.StopCoroutine(_currentTransition);
                _currentTransition = null;
            }
            if (_exitRoutine != null)
            {
                _engine.StopCoroutine(_exitRoutine);
                _exitRoutine = null;
            }
            if (_enterRoutine != null)
            {
                _engine.StopCoroutine(_enterRoutine);
                _enterRoutine = null;
            }
            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(_currentState.State);
                }
            }

            _isInTransition = false;
        }
    }
Пример #11
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];

        // Uncomment this to block loop transitions
        // 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);
    }
Пример #12
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);
    }
Пример #13
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;
        }
    }