Advertisement
herman_tulleken

C# State Machine

Jun 5th, 2015
394
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.29 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3.  
  4. namespace Gamelogic
  5. {
  6.     /// <summary>
  7.     /// A lightweight state machine.
  8.     /// </summary>
  9.     /// <remarks>
  10.     ///     <para>To use it: </para>
  11.     ///     <list type="bullet">
  12.     ///         <item>
  13.     ///             <description>Define your own label. Enums are probably the best
  14.     /// choice, but strings or occasionally useful too. Integer labels are useful when constructing
  15.     /// complicated state machines automatically (in loops, for example).</description>
  16.     ///         </item>
  17.     ///         <item>
  18.     ///             <description>Construct a new state machine, typically in a
  19.     /// MonoBehaviour's Start method.</description>
  20.     ///         </item>
  21.     ///         <item>
  22.     ///             <description>Add the various states with the appropriate delegates.
  23.     /// </description>
  24.     ///         </item>
  25.     ///         <item>
  26.     ///             <description>Call the state machine's Update method from the
  27.     /// MonoBehaviour's Update method.</description>
  28.     ///         </item>
  29.     ///         <item>
  30.     ///             <description>Set the CurrentState property on the state machine to transition.
  31.     /// (You can eitther set it from one of the state delegates, or from anywhere else.
  32.     /// </description>
  33.     ///         </item>
  34.     ///     </list>
  35.     ///     <para>When a state is changed, the OnStop on existing state is called, then the
  36.     /// OnStart of the new state, and from there on OnUpdate of the new state each time
  37.     /// the update is called.</para>
  38.     /// </remarks>
  39.     /// <typeparam name="TLabel">The label type of this state machine. Enums are common,
  40.     /// but strings or int are other possibilities.</typeparam>
  41.     [Version(1)]
  42.     public class StateMachine<TLabel>
  43.     {
  44.         private class State
  45.         {
  46.             public readonly Action onStart;
  47.             public readonly Action onUpdate;
  48.             public readonly Action onStop;
  49.             public readonly TLabel label;
  50.  
  51.             public State(TLabel label, Action onStart, Action onUpdate, Action onStop)
  52.             {
  53.                 this.onStart = onStart;
  54.                 this.onUpdate = onUpdate;
  55.                 this.onStop = onStop;
  56.                 this.label = label;
  57.             }
  58.         }
  59.  
  60.         private readonly Dictionary<TLabel, State> stateDictionary;
  61.         private State currentState;
  62.  
  63.         /// <summary>
  64.         /// Returns the label of the current state.
  65.         /// </summary>
  66.         public TLabel CurrentState
  67.         {
  68.             get { return currentState.label; }
  69.  
  70.             [Version(1, 2)]
  71.             set { ChangeState(value); }
  72.         }
  73.  
  74.         /// <summary>
  75.         /// Constructs a new StateMachine.
  76.         /// </summary>
  77.         public StateMachine()
  78.         {
  79.             stateDictionary = new Dictionary<TLabel, State>();
  80.         }
  81.  
  82.         /// <summary>
  83.         /// Adds a state, and the delegates that should run
  84.         /// when the state starts, stops,
  85.         /// and when the state machine is updated.
  86.         ///
  87.         /// Any delegate can be null, and wont be executed.
  88.         /// </summary>
  89.         /// <param name="label">The name of the state to add.</param>
  90.         /// <param name="onStart">The action performed when the state is entered.</param>
  91.         /// <param name="onUpdate">The action performed when the state machine is updated in the given state.</param>
  92.         /// <param name="onStop">The action performed when the state machine is left.</param>
  93.         public void AddState(TLabel label, Action onStart, Action onUpdate, Action onStop)
  94.         {
  95.             stateDictionary[label] = new State(label, onStart, onUpdate, onStop);
  96.         }
  97.  
  98.         public void AddState(TLabel label, Action onStart, Action onUpdate)
  99.         {
  100.             AddState(label, onStart, onUpdate, null);
  101.         }
  102.  
  103.         public void AddState(TLabel label, Action onStart)
  104.         {
  105.             AddState(label, onStart, null);
  106.         }
  107.  
  108.         public void AddState(TLabel label)
  109.         {
  110.             AddState(label, null);
  111.         }
  112.        
  113.         /// <summary>
  114.         /// Adds a sub state machine for the given state.
  115.         ///
  116.         /// The sub state machine need not be updated, as long as this state machine
  117.         /// is being updated.
  118.         /// </summary>
  119.         /// <typeparam name="TSubstateLabel">The type of the submachine.</typeparam>
  120.         /// <param name="label">The name of the state to add.</param>
  121.         /// <param name="subMachine">The submachine that will run during the given state.</param>
  122.         /// <param name="subMachineStartState">The starting state of the submachine.</param>
  123.         [Version(1, 4)]
  124.         public void AddState<TSubstateLabel>(TLabel label, StateMachine<TSubstateLabel> subMachine,
  125.             TSubstateLabel subMachineStartState)
  126.         {
  127.             AddState(
  128.                 label,
  129.                 () => subMachine.ChangeState(subMachineStartState),
  130.                 subMachine.Update);
  131.         }
  132.  
  133.         /// <summary>
  134.         /// Changes the state from the existing one to the state with the given label.
  135.         ///
  136.         /// It is legal (and useful) to transition to the same state, in which case the
  137.         /// current state's onStop action is called, the onstart ation is called, and the
  138.         /// state keeps on updating as before. The behviour is exactly the same as switching to
  139.         /// a new state.
  140.         /// </summary>
  141.         /// <param name="newState"></param>
  142.         private void ChangeState(TLabel newState)
  143.         {
  144.             if (currentState != null && currentState.onStop != null)
  145.             {
  146.                 currentState.onStop();
  147.             }
  148.  
  149.             currentState = stateDictionary[newState];
  150.  
  151.             if (currentState.onStart != null)
  152.             {
  153.                 currentState.onStart();
  154.             }
  155.         }
  156.  
  157.         /// <summary>
  158.         /// This method should be called every frame.
  159.         /// </summary>
  160.         public void Update()
  161.         {
  162.             if (currentState != null && currentState.onUpdate != null)
  163.             {
  164.                 currentState.onUpdate();
  165.             }
  166.         }
  167.  
  168.         /// <summary>
  169.         /// This method should be called every frame.
  170.         /// </summary>
  171.         /// <returns></returns>
  172.         public override string ToString()
  173.         {
  174.             return CurrentState.ToString();
  175.         }
  176.     }
  177.  
  178.     /// <summary>
  179.     /// This class is a state machine that has the ability to remember previous states
  180.     /// and transition back to them (FIFO).
  181.     /// </summary>
  182.     /// <typeparam name="TLabel">The type of state labels.</typeparam>
  183.     [Version(1, 4)]
  184.     public class PushdownAutomaton<TLabel> : StateMachine<TLabel>
  185.     {
  186.         private readonly Stack<TLabel> stateHistory;
  187.  
  188.         public PushdownAutomaton()
  189.         {
  190.             stateHistory = new Stack<TLabel>();
  191.         }
  192.  
  193.         /// <summary>
  194.         /// Pushes the current state onto the stack, and transitions to the next state.
  195.         /// </summary>
  196.         /// <param name="nextState"></param>
  197.         public void Push(TLabel nextState)
  198.         {
  199.             stateHistory.Push(CurrentState);
  200.            
  201.             CurrentState = nextState;
  202.         }
  203.  
  204.         /// <summary>
  205.         /// Pops a state from the stack and switches to it.
  206.         /// </summary>
  207.         public void Pop()
  208.         {
  209.             if (stateHistory.Count > 0)
  210.             {
  211.                 CurrentState = stateHistory.Pop();
  212.             }
  213.         }
  214.     }
  215. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement