Example #1
0
        private IEnumerator CoTransitToNewState(StateMapping newState, TransitionOptions options)
        {
            // Cache this so that we can overwrite it and hijack a transition.
            _destinationState = newState;

            if (_currentState != null)
            {
                if (_currentState.layout.hasExitCoroutine)
                {
                    _exitCoroutine = _currentState.exitCoroutine();

                    // Don't wait for exit if we are overwriting
                    if (_exitCoroutine != null && (options & TransitionOptions.Overwrite) == 0)
                    {
                        yield return(_runner.StartCoroutine(_exitCoroutine));
                    }

                    _exitCoroutine = null;
                }
                else
                {
                    _currentState.exitCall();
                }

                _currentState.finallyCall();
            }

            _lastState      = _currentState;
            _currentState   = _destinationState;
            _stateChangedAt = Time.time;

            if (_currentState != null)
            {
                if (_currentState.layout.hasEnterCoroutine)
                {
                    _enterCoroutine = _currentState.enterCoroutine();

                    if (_enterCoroutine != null)
                    {
                        yield return(_runner.StartCoroutine(_enterCoroutine));
                    }

                    _enterCoroutine = null;
                }
                else
                {
                    _currentState.enterCall();
                }

                // Broadcast change only after enter transition has begun.
                Changed?.Invoke(_lastState.state != null ? (T)_lastState.state : default(T), (T)_currentState.state);
            }

            _isInTransition = false;
        }
Example #2
0
        private IEnumerator CoWaitForPreviousTranstionAndTransitToNext(StateMapping nextState)
        {
            // Waiting for previous transition is completed.
            while (_isInTransition)
            {
                yield return(null);
            }

            // Workaround for unwanted state tansition churns.
            while (Time.timeScale <= 0f)
            {
                yield return(null);
            }

            Transit((T)nextState.state);
        }
Example #3
0
        public StateMachine(StateMachineRunner runner, MonoBehaviour monoComponent)
        {
            _runner        = runner;
            _monoComponent = monoComponent;

            // Cache state layout lookup for specified Component's type.
            var layoutLookup = StateMappingLayoutCache.Get(monoComponent.GetType(), typeof(T));

            _stateLookup = new Dictionary <object, StateMapping>(layoutLookup.Lookup.Count);

            foreach (var layoutPair in layoutLookup.Lookup)
            {
                var layout = layoutPair.Value;

                var mapping = new StateMapping(layout.state, layout);
                _stateLookup.Add(mapping.state, mapping);

                // *_Enter callback
                if (layout.enterMethod != null)
                {
                    if (layout.hasEnterCoroutine)
                    {
                        mapping.enterCoroutine = CreateDelegate <Func <IEnumerator> >(layout.enterMethod, monoComponent);
                    }
                    else
                    {
                        mapping.enterCall = CreateDelegate <Action>(layout.enterMethod, monoComponent);
                    }
                }

                // *_Exit callback
                if (layout.exitMethod != null)
                {
                    if (layout.hasExitCoroutine)
                    {
                        mapping.exitCoroutine = CreateDelegate <Func <IEnumerator> >(layout.exitMethod, monoComponent);
                    }
                    else
                    {
                        mapping.exitCall = CreateDelegate <Action>(layout.exitMethod, monoComponent);
                    }
                }

                // *_Finally callback
                if (layout.finallyMethod != null)
                {
                    mapping.finallyCall = CreateDelegate <Action>(layout.finallyMethod, monoComponent);
                }

                // *_Update callback
                if (layout.updateMethod != null)
                {
                    mapping.updateCall = CreateDelegate <Action>(layout.updateMethod, monoComponent);
                }

                // *_LateUpdate callback
                if (layout.lateUpdateMethod != null)
                {
                    mapping.lateUpdateCall = CreateDelegate <Action>(layout.lateUpdateMethod, monoComponent);
                }

                // *_FixedUpdate callback
                if (layout.fixedUpdateMethod != null)
                {
                    mapping.fixedUpdateCall = CreateDelegate <Action>(layout.fixedUpdateMethod, monoComponent);
                }

                // *_OnCollision callback
                //if (layout.onCollisionEnterMethod != null)
                //{
                //	mapping.onCollisionEnterCall = CreateDelegate<Action>(layout.onCollisionEnterMethod, monoComponent);
                //}
            }

            // Create nil state mapping
            _currentState = new StateMapping(null, StateMappingLayout.Null);

            _stateChangedAt = Time.time;
        }
Example #4
0
        public void Transit(T newState, TransitionOptions options)
        {
            if (_stateLookup == null)
            {
                throw new Exception("States have not been configured, please call initialized before trying to set state");
            }

            StateMapping nextState = null;

            if (!_stateLookup.TryGetValue(newState, out nextState))
            {
                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");
            }

            // Self transition.
            if ((options & TransitionOptions.AllowSelfTransition) == 0)
            {
                if (_currentState == nextState)
                {
                    return;
                }
            }

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

            if ((options & TransitionOptions.Overwrite) == 0)
            {
                if (_isInTransition)
                {
                    // We are already exiting current state on our way to our previous target state
                    if (_exitCoroutine != null)
                    {
                        // Overwrite with our new target
                        _destinationState = nextState;
                        return;
                    }

                    // We are already entering our previous target state.
                    // Need to wait for that to finish and call the exit routine.
                    if (_enterCoroutine != null)
                    {
                        // Damn, I need to test this hard
                        _queuedChange = CoWaitForPreviousTranstionAndTransitToNext(nextState);
                        _runner.StartCoroutine(_queuedChange);
                        return;
                    }
                }
            }
            else
            {
                if (_currentTransition != null)
                {
                    _runner.StopCoroutine(_currentTransition);
                }

                if (_exitCoroutine != null)
                {
                    _runner.StopCoroutine(_exitCoroutine);
                }

                if (_enterCoroutine != null)
                {
                    _runner.StopCoroutine(_enterCoroutine);
                }
            }

            if ((_currentState != null && _currentState.layout.hasExitCoroutine) || nextState.layout.hasEnterCoroutine)
            {
                _isInTransition    = true;
                _currentTransition = CoTransitToNewState(nextState, options);
                _runner.StartCoroutine(_currentTransition);
            }
            else //Same frame transition, no coroutines are present
            {
                if (_currentState != null)
                {
                    _currentState.exitCall();
                    _currentState.finallyCall();
                }

                _lastState      = _currentState;
                _currentState   = nextState;
                _stateChangedAt = Time.time;

                if (_currentState != null)
                {
                    _currentState.enterCall();

                    Changed?.Invoke(_lastState.state != null ? (T)_lastState.state : default(T), (T)_currentState.state);
                }

                _isInTransition = false;
            }
        }