daily pastebin goal
24%
SHARE
TWEET

C# State Machine

herman_tulleken Jun 5th, 2015 293 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top