diff --git a/StateMachine.cs b/StateMachine.cs new file mode 100644 index 0000000..7748652 --- /dev/null +++ b/StateMachine.cs @@ -0,0 +1,134 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using ZelleGames.Log; + +namespace ZelleGames.StateMachine +{ + /// + /// Generic abstract state machine with OnEnter and OnExit features + /// + public abstract class StateMachine : MonoBehaviour + { + /// + /// Readonly State currently executing + /// + public State CurrentState { get; protected set; } + + /// + /// Drive the current state if present + /// + private void Update() + { + if (CurrentState != null) + { + CurrentState.Update(Time.deltaTime); + } + } + + /// + /// Drive the current state's FixedUpdate method if state is present + /// + private void FixedUpdate() + { + if (CurrentState != null) + { + CurrentState.FixedUpdate(Time.fixedDeltaTime); + } + } + + /// + /// Public facing function to change state based on a transition. + /// Relies on an implementation of + /// + /// The transition to pass in. Validitiy depends on the implementation of GetNewState + public void TransitionState(Transition t) + { + var newState = GetNewState(t); + if (newState != null) + { + CurrentState?.OnExit(); + CurrentState = newState; + CurrentState.StateMachine = this; + CurrentState.OnEnter(); + } else + { + Logging.Log(LogLevel.WARN, $"Could not transition from {CurrentState} with {t}"); + } + } + + /// + /// Load a new state without consideration of the GetNewState function. + /// This is sessentially the "goto" of state machine management, use only if absolutely needed. + /// + /// + public void ForceState(State s) + { + CurrentState.OnExit(); + CurrentState = s; + CurrentState.OnEnter(); + } + + /// + /// Derived classes should provide an implementation that returns a new depending + /// on the Transition passed in. + /// + /// + /// The new state for a valid transition for the current state, + /// Null otherwise to maintain current state + protected abstract State GetNewState(Transition t); + } + + /// + /// Transitions are passed into the state machine to move from one state to the next. + /// + public abstract class Transition + { + /// + /// Generic object that can contain any information this transition needs to pass into the next state + /// + public object Context { get; } + + public Transition() : this(null){} + + public Transition(object ctx) + { + Context = ctx; + } + } + + /// + /// Describes a finite state for a + /// in Unity. + /// + public abstract class State + { + /// + /// Reference to the parent statemachine. Derived classes can cast this reference into their specialized + /// class for specific statemachine functionality, or use this reference as is to operate on the base class + /// + public StateMachine StateMachine; + + /// + /// Called every frame by the + /// + /// + public abstract void Update(float deltaTime); + + /// + /// Called according to the Physics timing configuration by the + /// + /// + public virtual void FixedUpdate(float fixedDeltaTime) { } + + /// + /// Called on the frame the machine switches to this state + /// + public virtual void OnEnter() { } + + /// + /// Called on the last frame before this state is replaced. + /// + public virtual void OnExit() { } + } +} \ No newline at end of file