// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; namespace FlaxEngine.Utilities { /// /// State machine logic pattern /// public abstract class StateMachine { /// /// The current state. /// protected State currentState; /// /// The states. /// protected readonly List states = new List(); /// /// Gets the current state. /// public State CurrentState => currentState; /// /// Occurs when state is being changed. /// public event Action StateChanging; /// /// Occurs when state gets changed. /// public event Action StateChanged; /// /// Gets the states (read-only). /// public IReadOnlyList States => states; /// /// Gets state of given type. /// /// The type of the state. public State GetState() { return states.Find(x => x.GetType() == typeof(TStateType)); } /// /// Goes to the state. /// /// The type of the state. /// Cannot find state of given type. public void GoToState() { var state = states.Find(x => x.GetType() == typeof(TStateType)); if (state == null) throw new InvalidOperationException($"Cannot find state {typeof(TStateType)}."); GoToState(state); } /// /// Goes to the state. /// /// The target state. /// state public virtual void GoToState(State state) { if (state == null) throw new ArgumentNullException(nameof(state)); // Prevent from entering the same state if (state == currentState) return; // Check if cannot leave current state if (currentState != null && !currentState.CanExit(state)) return; // Check if cannot enter new state if (!state.CanEnter()) return; // Change state SwitchState(state); } /// /// Adds the state. /// /// The state. public virtual void AddState(State state) { if (state.owner == this) throw new InvalidOperationException("Cannot add already registered state."); states.Add(state); state.owner = this; } /// /// Removes the state. /// /// The state. public virtual void RemoveState(State state) { if (state.owner == null) throw new InvalidOperationException("Cannot remove unregistered state."); states.Remove(state); state.owner = null; } /// /// Switches the state. /// /// Then next state. protected virtual void SwitchState(State nextState) { StateChanging?.Invoke(); currentState?.OnExit(nextState); currentState = nextState; currentState?.OnEnter(); StateChanged?.Invoke(); } } }