using System; using System.Collections; using System.Collections.Generic; using Unity.HLODSystem.Utils; using UnityEngine; namespace Unity.HLODSystem { public class FSM where T : struct { private T m_currentState = default; private T m_lastState = default; private T m_transactionTargetState = default; //event ordering //exiting -> entering -> exited -> entered class Functions { public Action EnteringFunction; public Func IsReadyToEnterFunction; public Action EnteredFunction; public Action ExitingFunction; public Func IsReadyToExitFunction; public Action ExitedFunction; } private Dictionary m_functions = new Dictionary(); public T CurrentState => m_currentState; public T LastState => m_lastState; public void Update() { //transaction still progressing. have to check to finish. if (Compare(m_currentState, m_transactionTargetState) == false) { if (GetFunctions(m_currentState).IsReadyToExitFunction?.Invoke() == false) return; if (GetFunctions(m_transactionTargetState).IsReadyToEnterFunction?.Invoke() == false) return; //the transaction has been finished. GetFunctions(m_currentState).ExitedFunction?.Invoke(); GetFunctions(m_transactionTargetState).EnteredFunction?.Invoke(); m_currentState = m_transactionTargetState; } Debug.Assert(Compare(m_currentState, m_transactionTargetState)); //Here the transaction is always complete. //We have to check to start a new transaction. if (Compare(m_currentState, m_lastState) == false) { StartTransaction(m_currentState, m_lastState); } } public void ChangeState(T state) { m_lastState = state; if (Compare(m_currentState, m_lastState)) return; //it means, completed the last transaction. we should do it immediately. if (Compare(m_currentState, m_transactionTargetState)) { StartTransaction(m_currentState, m_lastState); } } public void RegisterEnteringFunction(T state, Action func) { GetFunctions(state).EnteringFunction = func; } public void UnregisterEnteringFunction(T state) { GetFunctions(state).EnteringFunction = null; } public void RegisterIsReadyToEnterFunction(T state, Func func) { GetFunctions(state).IsReadyToEnterFunction = func; } public void UnregisterIsReadyToEnterFunction(T state) { GetFunctions(state).IsReadyToEnterFunction = null; } public void RegisterEnteredFunction(T state, Action func) { GetFunctions(state).EnteredFunction = func; } public void UnregisterEnteredFunction(T state) { GetFunctions(state).EnteredFunction = null; } public void RegisterExitingFunction(T state, Action func) { GetFunctions(state).ExitingFunction = func; } public void UnregisterExitingFunction(T state) { GetFunctions(state).ExitingFunction = null; } public void RegisterIsReadyToExitFunction(T state, Func func) { GetFunctions(state).IsReadyToExitFunction = func; } public void UnregisterIsReadyToExitFunction(T state) { GetFunctions(state).IsReadyToExitFunction = null; } public void RegisterExitedFunction(T state, Action func) { GetFunctions(state).ExitedFunction = func; } public void UnregisterExitedFunction(T state) { GetFunctions(state).ExitedFunction = null; } private Functions GetFunctions(T state) { if ( m_functions.ContainsKey(state) == false ) m_functions.Add(state, new Functions()); return m_functions[state]; } private void StartTransaction(T current, T target) { m_transactionTargetState = target; GetFunctions(current).ExitingFunction?.Invoke(); GetFunctions(target).EnteringFunction?.Invoke(); } private static bool Compare(T lhs, T rhs) { return EqualityComparer.Default.Equals(lhs, rhs); } } }