No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SimpleStateMachine.cs 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // From unity/unity's UnityEditor.Connect at 1a53aca
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace UnityEditor.Purchasing
  6. {
  7. /// <summary>
  8. /// A very lightweight state machine which uses generics as events
  9. /// (unless you have specific needs, enums are recommended) and states
  10. /// (either instance of the base state or your own extensions).
  11. /// </summary>
  12. class SimpleStateMachine<T>
  13. {
  14. readonly HashSet<T> m_Events = new HashSet<T>();
  15. readonly Dictionary<string, State> m_StateByName = new Dictionary<string, State>();
  16. bool m_Initialized;
  17. /// <summary>
  18. /// The current state
  19. /// </summary>
  20. public State currentState { get; private set; }
  21. /// <summary>
  22. /// Initializes the state machine.
  23. /// This should be called after instantiating the state machine and instantiating the initial state.
  24. /// Since the state machine needs an initial state instance and the state needs a state machine instance, the
  25. /// proper order is:
  26. /// 1- instantiate state machine
  27. /// 2- instantiate initial state
  28. /// 3- initialize state machine with initial state
  29. /// A state machine cannot be initialized multiple times. Further attempts will be ignored.
  30. /// </summary>
  31. /// <param name="initialState"></param>
  32. public void Initialize(State initialState)
  33. {
  34. if (!m_Initialized)
  35. {
  36. if (!StateExists(initialState))
  37. {
  38. AddState(initialState);
  39. }
  40. m_Initialized = true;
  41. currentState = initialState;
  42. currentState.EnterState();
  43. }
  44. }
  45. /// <summary>
  46. /// Clears the current state
  47. /// Allows for a full redraw on the state machine in the activate action without reallocating memory
  48. /// </summary>
  49. public void ClearCurrentState()
  50. {
  51. if (m_Initialized)
  52. {
  53. currentState = null;
  54. m_Initialized = false;
  55. }
  56. }
  57. /// <summary>
  58. /// Adds a new event to the state machine
  59. /// Don't forget that events are sent to states. So when adding an event to the state machine,
  60. /// it generally means that this event should also be configured on some states by using
  61. /// mySimpleStateMachineState.ModifyActionForEvent(myEvent, myHandler);
  62. /// </summary>
  63. /// <param name="simpleStateMachineEvent">the event</param>
  64. public void AddEvent(T simpleStateMachineEvent)
  65. {
  66. m_Events.Add(simpleStateMachineEvent);
  67. }
  68. public bool EventExists(T simpleStateMachineEvent)
  69. {
  70. foreach (var knownEvent in m_Events)
  71. {
  72. if (knownEvent.Equals(simpleStateMachineEvent))
  73. {
  74. return true;
  75. }
  76. }
  77. return false;
  78. }
  79. /// <summary>
  80. /// Gets a copy of the state machine events.
  81. /// Copy means you cannot alter the state machine by altering this list.
  82. /// It's a shallow copy though, be careful what you do with the list entries
  83. /// </summary>
  84. /// <returns>a copy of the state machine events</returns>
  85. public List<T> GetEvents()
  86. {
  87. return new List<T>(m_Events);
  88. }
  89. /// <summary>
  90. /// Adds a new state to the state machine.
  91. /// This state can be generic (straight instance of SimpleStateMachineState) or an extended version if you need
  92. /// to override the EnterState() method of the SimpleStateMachineState.
  93. /// A state should react to events. Configure the state by using ModifyActionForEvent
  94. /// </summary>
  95. /// <param name="state"></param>
  96. public void AddState(State state)
  97. {
  98. m_StateByName.Add(state.name, state);
  99. }
  100. /// <summary>
  101. /// This method is exposed to allow states to return a new state when an event action is completed successfully
  102. /// and a transition is made to another state.
  103. /// </summary>
  104. /// <param name="stateName">The name of the state to find</param>
  105. /// <returns>The state; or null if none found</returns>
  106. public State GetStateByName(string stateName)
  107. {
  108. return m_StateByName.ContainsKey(stateName) ? m_StateByName[stateName] : null;
  109. }
  110. public bool StateExists(State state)
  111. {
  112. return m_StateByName.ContainsKey(state.name);
  113. }
  114. /// <summary>
  115. /// Gets a shallow copy of the state machine states.
  116. /// Copy means you cannot alter the state machine by altering this list
  117. /// (but changing the configuration of a state will modify the state machine: shallow copy)
  118. /// </summary>
  119. /// <returns>a copy of the state machine states</returns>
  120. public List<State> GetStates()
  121. {
  122. return new List<State>(m_StateByName.Values);
  123. }
  124. /// <summary>
  125. /// Call this method when an event occurs on the state machine.
  126. /// This will often result in a state change, but it's not mandatory.
  127. /// Nothing will happen if the event does not exist within the state machine events'
  128. /// </summary>
  129. /// <param name="simpleStateMachineEvent">The event to process</param>
  130. public void ProcessEvent(T simpleStateMachineEvent)
  131. {
  132. if (m_Initialized && EventExists(simpleStateMachineEvent))
  133. {
  134. var previousState = currentState;
  135. currentState = currentState.GetActionForEvent(simpleStateMachineEvent).Invoke(simpleStateMachineEvent);
  136. if (currentState != previousState)
  137. {
  138. if (currentState != null)
  139. {
  140. currentState.EnterState();
  141. }
  142. else
  143. {
  144. Debug.LogError("SimpleStateMachine.ProcessEvent: " + L10n.Tr("Attempting to change to an undefined state. Contact Unity Support."));
  145. }
  146. }
  147. }
  148. }
  149. /// <summary>
  150. /// Base state for a simple state machine. It can be used as-is or extended to gain additional functionality
  151. /// </summary>
  152. internal class State
  153. {
  154. readonly List<ActionForEvent> m_ActionForEvent = new List<ActionForEvent>();
  155. /// <summary>
  156. /// Access to the state machine. Mostly to GetStateByName when transitioning from one state to another
  157. /// </summary>
  158. protected SimpleStateMachine<T> stateMachine { get; }
  159. public string name { get; }
  160. public State(string name, SimpleStateMachine<T> simpleStateMachine)
  161. {
  162. this.name = name;
  163. stateMachine = simpleStateMachine;
  164. }
  165. public virtual bool ActionExistsForEvent(T simpleStateMachineEvent)
  166. {
  167. foreach (var actionForEvent in m_ActionForEvent)
  168. {
  169. if (actionForEvent.simpleStateMachineEvent.Equals(simpleStateMachineEvent))
  170. {
  171. return true;
  172. }
  173. }
  174. return false;
  175. }
  176. /// <summary>
  177. /// Updates or Inserts a new action to execute when an event occurs when the state machine is at this state.
  178. /// </summary>
  179. /// <param name="simpleStateMachineEvent">The event</param>
  180. /// <param name="transitionAction">The action to accomplish when the specified event occurs</param>
  181. public virtual void ModifyActionForEvent(T simpleStateMachineEvent, Func<T, State> transitionAction)
  182. {
  183. foreach (var actionForEvent in m_ActionForEvent)
  184. {
  185. if (actionForEvent.simpleStateMachineEvent.Equals(simpleStateMachineEvent))
  186. {
  187. actionForEvent.action = transitionAction;
  188. return;
  189. }
  190. }
  191. m_ActionForEvent.Add(new ActionForEvent(simpleStateMachineEvent, transitionAction));
  192. }
  193. /// <summary>
  194. /// This method is called by the state machine when an event occurs. By default,
  195. /// if the state machine does not find an action to execute, the state will remain unchanged
  196. /// and the event will be ignored.
  197. /// </summary>
  198. /// <param name="simpleStateMachineEvent">the event we search an action for</param>
  199. /// <returns>an action to execute; can be DoNothing if ModifyActionForEvent wasn't done for this event</returns>
  200. public virtual Func<T, State> GetActionForEvent(T simpleStateMachineEvent)
  201. {
  202. foreach (var actionForEvent in m_ActionForEvent)
  203. {
  204. if (actionForEvent.simpleStateMachineEvent.Equals(simpleStateMachineEvent))
  205. {
  206. return actionForEvent.action;
  207. }
  208. }
  209. return DoNothing;
  210. }
  211. /// <summary>
  212. /// Default action taken when no handler is found for an event.
  213. /// Will simply return the current state and thus, does nothing
  214. /// </summary>
  215. /// <returns></returns>
  216. State DoNothing(T simpleStateMachineEvent)
  217. {
  218. return this;
  219. }
  220. /// <summary>
  221. /// This method will be called by the state machine when the a state becomes active.
  222. /// It allows to do a common operation on the current state without having all the other states repeat this
  223. /// code within their transition actions.
  224. /// </summary>
  225. public virtual void EnterState() { }
  226. class ActionForEvent
  227. {
  228. internal T simpleStateMachineEvent { get; }
  229. internal Func<T, State> action { get; set; }
  230. internal ActionForEvent(T simpleStateMachineEvent, Func<T, State> action)
  231. {
  232. this.simpleStateMachineEvent = simpleStateMachineEvent;
  233. this.action = action;
  234. }
  235. }
  236. }
  237. }
  238. }