GraphModifier.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. namespace Pathfinding {
  4. /// <summary>
  5. /// GraphModifier is used for modifying graphs or processing graph data based on events.
  6. /// This class is a simple container for a number of events.
  7. ///
  8. /// Warning: Some events will be called both in play mode <b>and in editor mode</b> (at least the scan events).
  9. /// So make sure your code handles both cases well. You may choose to ignore editor events.
  10. /// See: Application.IsPlaying
  11. /// </summary>
  12. [ExecuteInEditMode]
  13. public abstract class GraphModifier : VersionedMonoBehaviour {
  14. /// <summary>All active graph modifiers</summary>
  15. private static GraphModifier root;
  16. private GraphModifier prev;
  17. private GraphModifier next;
  18. /// <summary>Unique persistent ID for this component, used for serialization</summary>
  19. [SerializeField]
  20. [HideInInspector]
  21. protected ulong uniqueID;
  22. /// <summary>Maps persistent IDs to the component that uses it</summary>
  23. protected static Dictionary<ulong, GraphModifier> usedIDs = new Dictionary<ulong, GraphModifier>();
  24. protected static List<T> GetModifiersOfType<T>() where T : GraphModifier {
  25. var current = root;
  26. var result = new List<T>();
  27. while (current != null) {
  28. var cast = current as T;
  29. if (cast != null) result.Add(cast);
  30. current = current.next;
  31. }
  32. return result;
  33. }
  34. public static void FindAllModifiers () {
  35. var allModifiers = FindObjectsOfType(typeof(GraphModifier)) as GraphModifier[];
  36. for (int i = 0; i < allModifiers.Length; i++) {
  37. if (allModifiers[i].enabled) allModifiers[i].OnEnable();
  38. }
  39. }
  40. /// <summary>GraphModifier event type</summary>
  41. public enum EventType {
  42. PostScan = 1 << 0,
  43. PreScan = 1 << 1,
  44. LatePostScan = 1 << 2,
  45. PreUpdate = 1 << 3,
  46. PostUpdate = 1 << 4,
  47. PostCacheLoad = 1 << 5
  48. }
  49. /// <summary>Triggers an event for all active graph modifiers</summary>
  50. public static void TriggerEvent (GraphModifier.EventType type) {
  51. if (!Application.isPlaying) {
  52. FindAllModifiers();
  53. }
  54. GraphModifier c = root;
  55. switch (type) {
  56. case EventType.PreScan:
  57. while (c != null) { c.OnPreScan(); c = c.next; }
  58. break;
  59. case EventType.PostScan:
  60. while (c != null) { c.OnPostScan(); c = c.next; }
  61. break;
  62. case EventType.LatePostScan:
  63. while (c != null) { c.OnLatePostScan(); c = c.next; }
  64. break;
  65. case EventType.PreUpdate:
  66. while (c != null) { c.OnGraphsPreUpdate(); c = c.next; }
  67. break;
  68. case EventType.PostUpdate:
  69. while (c != null) { c.OnGraphsPostUpdate(); c = c.next; }
  70. break;
  71. case EventType.PostCacheLoad:
  72. while (c != null) { c.OnPostCacheLoad(); c = c.next; }
  73. break;
  74. }
  75. }
  76. /// <summary>Adds this modifier to list of active modifiers</summary>
  77. protected virtual void OnEnable () {
  78. RemoveFromLinkedList();
  79. AddToLinkedList();
  80. ConfigureUniqueID();
  81. }
  82. /// <summary>Removes this modifier from list of active modifiers</summary>
  83. protected virtual void OnDisable () {
  84. RemoveFromLinkedList();
  85. }
  86. protected override void Awake () {
  87. base.Awake();
  88. ConfigureUniqueID();
  89. }
  90. void ConfigureUniqueID () {
  91. // Check if any other object is using the same uniqueID
  92. // In that case this object may have been duplicated
  93. GraphModifier usedBy;
  94. if (usedIDs.TryGetValue(uniqueID, out usedBy) && usedBy != this) {
  95. Reset();
  96. }
  97. usedIDs[uniqueID] = this;
  98. }
  99. void AddToLinkedList () {
  100. if (root == null) {
  101. root = this;
  102. } else {
  103. next = root;
  104. root.prev = this;
  105. root = this;
  106. }
  107. }
  108. void RemoveFromLinkedList () {
  109. if (root == this) {
  110. root = next;
  111. if (root != null) root.prev = null;
  112. } else {
  113. if (prev != null) prev.next = next;
  114. if (next != null) next.prev = prev;
  115. }
  116. prev = null;
  117. next = null;
  118. }
  119. protected virtual void OnDestroy () {
  120. usedIDs.Remove(uniqueID);
  121. }
  122. /// <summary>
  123. /// Called right after all graphs have been scanned.
  124. /// FloodFill and other post processing has not been done.
  125. ///
  126. /// Warning: Since OnEnable and Awake are called roughly in the same time, the only way
  127. /// to ensure that these scripts get this call when scanning in Awake is to
  128. /// set the Script Execution Order for AstarPath to some time later than default time
  129. /// (see Edit -> Project Settings -> Script Execution Order).
  130. /// TODO: Is this still relevant? A call to FindAllModifiers should have before this method is called
  131. /// so the above warning is probably not relevant anymore.
  132. ///
  133. /// See: OnLatePostScan
  134. /// </summary>
  135. public virtual void OnPostScan () {}
  136. /// <summary>
  137. /// Called right before graphs are going to be scanned.
  138. ///
  139. /// Warning: Since OnEnable and Awake are called roughly in the same time, the only way
  140. /// to ensure that these scripts get this call when scanning in Awake is to
  141. /// set the Script Execution Order for AstarPath to some time later than default time
  142. /// (see Edit -> Project Settings -> Script Execution Order).
  143. /// TODO: Is this still relevant? A call to FindAllModifiers should have before this method is called
  144. /// so the above warning is probably not relevant anymore.
  145. ///
  146. /// See: OnLatePostScan
  147. /// </summary>
  148. public virtual void OnPreScan () {}
  149. /// <summary>
  150. /// Called at the end of the scanning procedure.
  151. /// This is the absolute last thing done by Scan.
  152. /// </summary>
  153. public virtual void OnLatePostScan () {}
  154. /// <summary>
  155. /// Called after cached graphs have been loaded.
  156. /// When using cached startup, this event is analogous to OnLatePostScan and implementing scripts
  157. /// should do roughly the same thing for both events.
  158. /// </summary>
  159. public virtual void OnPostCacheLoad () {}
  160. /// <summary>Called before graphs are updated using GraphUpdateObjects</summary>
  161. public virtual void OnGraphsPreUpdate () {}
  162. /// <summary>
  163. /// Called after graphs have been updated using GraphUpdateObjects.
  164. /// Eventual flood filling has been done
  165. /// </summary>
  166. public virtual void OnGraphsPostUpdate () {}
  167. protected override void Reset () {
  168. base.Reset();
  169. // Create a new random 64 bit value (62 bit actually because we skip negative numbers, but that's still enough by a huge margin)
  170. var rnd1 = (ulong)Random.Range(0, int.MaxValue);
  171. var rnd2 = ((ulong)Random.Range(0, int.MaxValue) << 32);
  172. uniqueID = rnd1 | rnd2;
  173. usedIDs[uniqueID] = this;
  174. }
  175. }
  176. }