/************************************************************************************************************************/

        /// <summary>
        /// Checks if it is currently possible to enter the specified `state`. This requires
        /// <see cref="IState.CanExitState"/> on the <see cref="CurrentState"/> and
        /// <see cref="IState.CanEnterState"/> on the specified `state` to both return true.
        /// </summary>
        public bool CanSetState(TState state)
        {
#if UNITY_ASSERTIONS
            if (state == null && !AllowNullStates)
            {
                throw new ArgumentNullException(nameof(state), NullNotAllowed);
            }
#endif

            StateChange <TState> .Begin(CurrentState, state, out var previouslyActiveChange);

            try
            {
                if (CurrentState != null && !CurrentState.CanExitState)
                {
                    return(false);
                }

                if (state != null && !state.CanEnterState)
                {
                    return(false);
                }

                return(true);
            }
            finally
            {
                StateChange <TState> .End(previouslyActiveChange);
            }
        }
        /************************************************************************************************************************/

        /// <summary>
        /// Calls <see cref="IState.OnExitState"/> on the <see cref="CurrentState"/> then changes to the
        /// specified `state` and calls <see cref="IState.OnEnterState"/> on it.
        /// <para></para>
        /// This method does not check <see cref="IState.CanExitState"/> or
        /// <see cref="IState.CanEnterState"/>. To do that, you should use <see cref="TrySetState"/> instead.
        /// </summary>
        public void ForceSetState(TState state)
        {
#if UNITY_ASSERTIONS
            if (state == null && !AllowNullStates)
            {
                throw new ArgumentNullException(nameof(state), NullNotAllowed);
            }
#endif

            StateChange <TState> .Begin(CurrentState, state, out var previouslyActiveChange);

            try
            {
#if UNITY_EDITOR
                if (state is IOwnedState <TState> owned && owned.OwnerStateMachine != this)
                {
                    throw new InvalidOperationException(
                              $"Attempted to use a state in a machine that is not its owner." +
                              $"\n    State: {state}" +
                              $"\n    Machine: {this}");
                }
#endif

                CurrentState?.OnExitState();

                CurrentState = state;

                state?.OnEnterState();
            }
            finally
            {
                StateChange <TState> .End(previouslyActiveChange);
            }
        }
        /************************************************************************************************************************/

        /// <summary>
        /// Attempts to enter the specified `state` and returns true if successful.
        /// <para></para>
        /// This method does not check if the `state` is already the <see cref="CurrentState"/>. To do so, use
        /// <see cref="TrySetState"/> instead.
        /// </summary>
        public bool TryResetState(TState state)
        {
            if (!CanSetState(state))
            {
                return(false);
            }

            StateChange <TState> .Begin(CurrentState, state, out var previouslyActiveChange);

            try
            {
                ForceSetState(state);
                return(true);
            }
            finally
            {
                StateChange <TState> .End(previouslyActiveChange);
            }
        }
        /// <summary>Creates a new <see cref="StateMachine{TState}"/> and immediately enters the `defaultState`.</summary>
        /// <remarks>This calls <see cref="IState.OnEnterState"/> but not <see cref="IState.CanEnterState"/>.</remarks>
        public StateMachine(TState defaultState)
        {
#if UNITY_ASSERTIONS
            if (defaultState == null && !AllowNullStates)
            {
                throw new ArgumentNullException(nameof(defaultState), NullNotAllowed);
            }
#endif

            StateChange <TState> .Begin(null, defaultState, out var previouslyActiveChange);

            try
            {
                CurrentState = defaultState;
                defaultState.OnEnterState();
            }
            finally
            {
                StateChange <TState> .End(previouslyActiveChange);
            }
        }
Beispiel #5
0
        /************************************************************************************************************************/

        /// <summary>[<see cref="IDisposable"/>]
        /// Re-assigns the values of this change (which were the previous values from when it was created) to be the
        /// currently active change. See the constructor for recommended usage.
        /// </summary>
        /// <remarks>
        /// Usually this will be returning to default values (nulls), but if one state change causes another then the
        /// second one ending will return to the first which will then return to the defaults.
        /// </remarks>
        public void Dispose()
        {
            _Current = this;
        }