diff --git a/Runtime/Implementations/StateMachine.cs b/Runtime/Implementations/StateMachine.cs index 1aedc23..ee4d8c3 100644 --- a/Runtime/Implementations/StateMachine.cs +++ b/Runtime/Implementations/StateMachine.cs @@ -12,13 +12,13 @@ namespace Better.StateMachine.Runtime { [Serializable] - public class StateMachine : IStateMachine + public class StateMachine : IStateMachine where TState : BaseState - where TTransitionSequence : ISequence, new() + where TSequence : Sequence, new() { public event Action StateChanged; - private readonly TTransitionSequence _transitionSequence; + private readonly TSequence _sequence; private readonly Locator> _modulesLocator; private CancellationTokenSource _runningTokenSource; @@ -30,14 +30,14 @@ public class StateMachine : IStateMachine public Task TransitionTask => InTransition ? _stateChangeCompletionSource.Task : Task.CompletedTask; public TState CurrentState { get; protected set; } - public StateMachine(TTransitionSequence transitionSequence) + public StateMachine(TSequence sequence) { - if (transitionSequence == null) + if (sequence == null) { - throw new ArgumentNullException(nameof(transitionSequence)); + throw new ArgumentNullException(nameof(sequence)); } - _transitionSequence = transitionSequence; + _sequence = sequence; _modulesLocator = new(); } @@ -103,16 +103,16 @@ public virtual void Stop() #region States - public async Task ChangeStateAsync(TState newState, CancellationToken cancellationToken) + public async Task ChangeStateAsync(TState state, CancellationToken cancellationToken) { if (!ValidateRunning(true)) { return; } - if (newState == null) + if (state == null) { - DebugUtility.LogException(nameof(newState)); + DebugUtility.LogException(nameof(state)); return; } @@ -122,9 +122,9 @@ public async Task ChangeStateAsync(TState newState, CancellationToken cancellati var modules = _modulesLocator.GetElements(); foreach (var module in modules) { - if (!module.AllowChangeState(this, newState)) + if (!module.AllowChangeState(this, state)) { - var message = $"{module} not allow change state to {newState}"; + var message = $"{module} not allow change state to {state}"; Debug.LogWarning(message); return; @@ -133,10 +133,18 @@ public async Task ChangeStateAsync(TState newState, CancellationToken cancellati _transitionTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_runningTokenSource.Token, cancellationToken); _stateChangeCompletionSource = new TaskCompletionSource(); + var rootState = CurrentState; - OnStatePreChanged(newState); - CurrentState = await _transitionSequence.ChangingStateAsync(CurrentState, newState, _transitionTokenSource.Token); - OnStateChanged(CurrentState); + OnStatePreChanged(state); + await _sequence.PreProcessingAsync(rootState, state, cancellationToken); + + if (!cancellationToken.IsCancellationRequested + && await _sequence.ProcessingAsync(rootState, state, cancellationToken)) + { + CurrentState = state; + await _sequence.PostProcessingAsync(rootState, state, cancellationToken); + OnStateChanged(CurrentState); + } _stateChangeCompletionSource.TrySetResult(true); _stateChangeCompletionSource = null; @@ -284,7 +292,7 @@ protected bool ValidateRunning(bool targetState, bool logException = true) public class StateMachine : StateMachine> where TState : BaseState { - public StateMachine(DefaultSequence transitionSequence) : base(transitionSequence) + public StateMachine(DefaultSequence sequence) : base(sequence) { } @@ -296,7 +304,7 @@ public StateMachine() : this(new()) [Serializable] public class StateMachine : StateMachine { - public StateMachine(DefaultSequence transitionSequence) : base(transitionSequence) + public StateMachine(DefaultSequence sequence) : base(sequence) { } diff --git a/Runtime/Interfaces/IStateMachine.cs b/Runtime/Interfaces/IStateMachine.cs index 74349a9..0961329 100644 --- a/Runtime/Interfaces/IStateMachine.cs +++ b/Runtime/Interfaces/IStateMachine.cs @@ -17,7 +17,7 @@ public interface IStateMachine where TState : BaseState void Run(); bool InState() where T : TState; - Task ChangeStateAsync(TState newState, CancellationToken cancellationToken = default); + Task ChangeStateAsync(TState state, CancellationToken cancellationToken = default); void Stop(); public bool TryAddModule(TModule module) where TModule : Module; diff --git a/Runtime/Sequences/DefaultSequence.cs b/Runtime/Sequences/DefaultSequence.cs index 42b410d..fde8437 100644 --- a/Runtime/Sequences/DefaultSequence.cs +++ b/Runtime/Sequences/DefaultSequence.cs @@ -7,35 +7,43 @@ namespace Better.StateMachine.Runtime.Sequences { [Serializable] - public class DefaultSequence : ISequence where TState : BaseState + public class DefaultSequence : Sequence where TState : BaseState { - async Task ISequence.ChangingStateAsync(TState currentState, TState newState, CancellationToken cancellationToken) + protected internal override Task PreProcessingAsync(TState fromState, TState toState, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - Debug.LogWarning("Was canceled before the start"); - return default; - } + return Task.CompletedTask; + } - if (newState == currentState) + protected internal override async Task ProcessingAsync(TState fromState, TState toState, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) { - Debug.LogWarning($"{nameof(newState)} equaled {nameof(currentState)}, operation was cancelled"); - return currentState; + var message = "Was canceled before the start"; + Debug.LogWarning(message); + return false; } - if (currentState != null) + if (fromState != null) { - await currentState.ExitAsync(cancellationToken); + await fromState.ExitAsync(cancellationToken); if (cancellationToken.IsCancellationRequested) { - return default; + return false; } } - await newState.EnterAsync(cancellationToken); - if (cancellationToken.IsCancellationRequested) return default; + await toState.EnterAsync(cancellationToken); + + var success = !cancellationToken.IsCancellationRequested; + return success; + } + + protected internal override Task PostProcessingAsync(TState fromState, TState toState, CancellationToken cancellationToken) + { + fromState?.OnExited(); + toState?.OnEntered(); - return newState; + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/Runtime/Sequences/ISequence.cs b/Runtime/Sequences/ISequence.cs deleted file mode 100644 index 097efef..0000000 --- a/Runtime/Sequences/ISequence.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Better.StateMachine.Runtime.States; - -namespace Better.StateMachine.Runtime.Sequences -{ - public interface ISequence where TState : BaseState - { - public Task ChangingStateAsync(TState currentState, TState newState, CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/Runtime/Sequences/ISequence.cs.meta b/Runtime/Sequences/ISequence.cs.meta deleted file mode 100644 index aaa2c32..0000000 --- a/Runtime/Sequences/ISequence.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: b621c9ee12314b4aa6e3e2bcb0dcde68 -timeCreated: 1708236318 \ No newline at end of file diff --git a/Runtime/Sequences/Sequence.cs b/Runtime/Sequences/Sequence.cs new file mode 100644 index 0000000..092f7de --- /dev/null +++ b/Runtime/Sequences/Sequence.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Better.StateMachine.Runtime.States; + +namespace Better.StateMachine.Runtime.Sequences +{ + [Serializable] + public abstract class Sequence where TState : BaseState + { + protected internal abstract Task PreProcessingAsync(TState fromState, TState toState, CancellationToken cancellationToken); + protected internal abstract Task ProcessingAsync(TState fromState, TState toState, CancellationToken cancellationToken); + protected internal abstract Task PostProcessingAsync(TState fromState, TState toState, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Runtime/Sequences/Sequence.cs.meta b/Runtime/Sequences/Sequence.cs.meta new file mode 100644 index 0000000..1e2eed6 --- /dev/null +++ b/Runtime/Sequences/Sequence.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fe13c62aa9cb4e4e91db8f7342f17987 +timeCreated: 1716242526 \ No newline at end of file diff --git a/Runtime/States/BaseState.cs b/Runtime/States/BaseState.cs index 64d365b..2205b9d 100644 --- a/Runtime/States/BaseState.cs +++ b/Runtime/States/BaseState.cs @@ -9,10 +9,14 @@ public abstract class BaseState { /// Called once, when the StateMachine enters this state public abstract Task EnterAsync(CancellationToken token); + + public abstract void OnEntered(); /// Called once, when the State Machine exits from this state public abstract Task ExitAsync(CancellationToken token); - + + public abstract void OnExited(); + public override string ToString() { return GetType().Name; diff --git a/package.json b/package.json index 44f8d4a..140068b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.tdw.better.statemachine", "displayName": "Better State Machine", - "version": "0.1.8", + "version": "0.1.9", "unity": "2021.3", "description": " ", "dependencies": {