Created
February 16, 2021 22:59
-
-
Save johans2/53265975322959ed0ab69414277a9efa to your computer and use it in GitHub Desktop.
Hierarchical State Machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace HFSM { | |
public abstract class StateMachine { | |
private StateMachine currentSubState; | |
private StateMachine defaultSubState; | |
private StateMachine parent; | |
private Dictionary<Type, StateMachine> subStates = new Dictionary<Type, StateMachine>(); | |
private Dictionary<int, StateMachine> transitions = new Dictionary<int, StateMachine>(); | |
public void EnterStateMachine() { | |
OnEnter(); | |
if (currentSubState == null && defaultSubState != null) { | |
currentSubState = defaultSubState; | |
} | |
currentSubState?.EnterStateMachine(); | |
} | |
public void UpdateStateMachine() { | |
OnUpdate(); | |
currentSubState?.UpdateStateMachine(); | |
} | |
public void ExitStateMachine() { | |
currentSubState?.ExitStateMachine(); | |
OnExit(); | |
} | |
protected virtual void OnEnter() { } | |
protected virtual void OnUpdate() { } | |
protected virtual void OnExit() { } | |
public void LoadSubState(StateMachine subState) { | |
if (subStates.Count == 0) { | |
defaultSubState = subState; | |
} | |
subState.parent = this; | |
try { | |
subStates.Add(subState.GetType(), subState); | |
} | |
catch (ArgumentException) { | |
throw new DuplicateSubStateException($"State {GetType()} already contains a substate of type {subState.GetType()}"); | |
} | |
} | |
public void AddTransition(StateMachine from, StateMachine to, int trigger) { | |
if (!subStates.TryGetValue(from.GetType(), out _)) { | |
throw new InvalidTransitionException($"State {GetType()} does not have a substate of type {from.GetType()} to transition from."); | |
} | |
if (!subStates.TryGetValue(to.GetType(), out _)) { | |
throw new InvalidTransitionException($"State {GetType()} does not have a substate of type {to.GetType()} to transition from."); | |
} | |
try { | |
from.transitions.Add(trigger, to); | |
} | |
catch (ArgumentException) { | |
throw new DuplicateTransitionException($"State {from} already has a transition defined for trigger {trigger}"); | |
} | |
} | |
public void SendTrigger(int trigger) { | |
var root = this; | |
while (root?.parent != null) { | |
root = root.parent; | |
} | |
while (root != null) { | |
if (root.transitions.TryGetValue(trigger, out StateMachine toState)) { | |
root.parent?.ChangeSubState(toState); | |
return; | |
} | |
root = root.currentSubState; | |
} | |
throw new NeglectedTriggerException($"Trigger {trigger} was not consumed by any transition!"); | |
} | |
private void ChangeSubState(StateMachine state) { | |
currentSubState?.ExitStateMachine(); | |
var newState = subStates[state.GetType()]; | |
currentSubState = newState; | |
newState.EnterStateMachine(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment