BlockManager.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. namespace Pathfinding {
  4. using Pathfinding.Util;
  5. /// <summary>
  6. /// Manager for blocker scripts such as SingleNodeBlocker.
  7. ///
  8. /// This is part of the turn based utilities. It can be used for
  9. /// any game, but it is primarily intended for turn based games.
  10. ///
  11. /// See: TurnBasedAI
  12. /// See: turnbased (view in online documentation for working links)
  13. /// </summary>
  14. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_block_manager.php")]
  15. public class BlockManager : VersionedMonoBehaviour {
  16. /// <summary>Contains info on which SingleNodeBlocker objects have blocked a particular node</summary>
  17. Dictionary<GraphNode, List<SingleNodeBlocker> > blocked = new Dictionary<GraphNode, List<SingleNodeBlocker> >();
  18. public enum BlockMode {
  19. /// <summary>All blockers except those in the TraversalProvider.selector list will block</summary>
  20. AllExceptSelector,
  21. /// <summary>Only elements in the TraversalProvider.selector list will block</summary>
  22. OnlySelector
  23. }
  24. /// <summary>Blocks nodes according to a BlockManager</summary>
  25. public class TraversalProvider : ITraversalProvider {
  26. /// <summary>Holds information about which nodes are occupied</summary>
  27. readonly BlockManager blockManager;
  28. /// <summary>Affects which nodes are considered blocked</summary>
  29. public BlockMode mode { get; private set; }
  30. /// <summary>
  31. /// Blockers for this path.
  32. /// The effect depends on <see cref="mode"/>.
  33. ///
  34. /// Note that having a large selector has a performance cost.
  35. ///
  36. /// See: mode
  37. /// </summary>
  38. readonly List<SingleNodeBlocker> selector;
  39. public TraversalProvider (BlockManager blockManager, BlockMode mode, List<SingleNodeBlocker> selector) {
  40. if (blockManager == null) throw new System.ArgumentNullException("blockManager");
  41. if (selector == null) throw new System.ArgumentNullException("selector");
  42. this.blockManager = blockManager;
  43. this.mode = mode;
  44. this.selector = selector;
  45. }
  46. #region ITraversalProvider implementation
  47. public bool CanTraverse (Path path, GraphNode node) {
  48. // This first IF is the default implementation that is used when no traversal provider is used
  49. if (!node.Walkable || (path.enabledTags >> (int)node.Tag & 0x1) == 0) {
  50. return false;
  51. } else if (mode == BlockMode.OnlySelector) {
  52. return !blockManager.NodeContainsAnyOf(node, selector);
  53. } else {
  54. // assume mode == BlockMode.AllExceptSelector
  55. return !blockManager.NodeContainsAnyExcept(node, selector);
  56. }
  57. }
  58. public uint GetTraversalCost (Path path, GraphNode node) {
  59. // Same as default implementation
  60. return path.GetTagPenalty((int)node.Tag) + node.Penalty;
  61. }
  62. #endregion
  63. }
  64. void Start () {
  65. if (!AstarPath.active)
  66. throw new System.Exception("No AstarPath object in the scene");
  67. }
  68. /// <summary>True if the node contains any blocker which is included in the selector list</summary>
  69. public bool NodeContainsAnyOf (GraphNode node, List<SingleNodeBlocker> selector) {
  70. List<SingleNodeBlocker> blockersInNode;
  71. if (!blocked.TryGetValue(node, out blockersInNode)) {
  72. return false;
  73. }
  74. for (int i = 0; i < blockersInNode.Count; i++) {
  75. var inNode = blockersInNode[i];
  76. for (int j = 0; j < selector.Count; j++) {
  77. // Need to use ReferenceEquals because this code may be called from a separate thread
  78. // and the equality comparison that Unity provides is not thread safe
  79. if (System.Object.ReferenceEquals(inNode, selector[j])) {
  80. return true;
  81. }
  82. }
  83. }
  84. return false;
  85. }
  86. /// <summary>True if the node contains any blocker which is not included in the selector list</summary>
  87. public bool NodeContainsAnyExcept (GraphNode node, List<SingleNodeBlocker> selector) {
  88. List<SingleNodeBlocker> blockersInNode;
  89. if (!blocked.TryGetValue(node, out blockersInNode)) {
  90. return false;
  91. }
  92. for (int i = 0; i < blockersInNode.Count; i++) {
  93. var inNode = blockersInNode[i];
  94. bool found = false;
  95. for (int j = 0; j < selector.Count; j++) {
  96. // Need to use ReferenceEquals because this code may be called from a separate thread
  97. // and the equality comparison that Unity provides is not thread safe
  98. if (System.Object.ReferenceEquals(inNode, selector[j])) {
  99. found = true;
  100. break;
  101. }
  102. }
  103. if (!found) return true;
  104. }
  105. return false;
  106. }
  107. /// <summary>
  108. /// Register blocker as being present at the specified node.
  109. /// Calling this method multiple times will add multiple instances of the blocker to the node.
  110. ///
  111. /// Note: The node will not be blocked immediately. Instead the pathfinding
  112. /// threads will be paused and then the update will be applied. It is however
  113. /// guaranteed to be applied before the next path request is started.
  114. /// </summary>
  115. public void InternalBlock (GraphNode node, SingleNodeBlocker blocker) {
  116. AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
  117. List<SingleNodeBlocker> blockersInNode;
  118. if (!blocked.TryGetValue(node, out blockersInNode)) {
  119. blockersInNode = blocked[node] = ListPool<SingleNodeBlocker>.Claim();
  120. }
  121. blockersInNode.Add(blocker);
  122. }));
  123. }
  124. /// <summary>
  125. /// Remove blocker from the specified node.
  126. /// Will only remove a single instance, calling this method multiple
  127. /// times will remove multiple instances of the blocker from the node.
  128. ///
  129. /// Note: The node will not be unblocked immediately. Instead the pathfinding
  130. /// threads will be paused and then the update will be applied. It is however
  131. /// guaranteed to be applied before the next path request is started.
  132. /// </summary>
  133. public void InternalUnblock (GraphNode node, SingleNodeBlocker blocker) {
  134. AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
  135. List<SingleNodeBlocker> blockersInNode;
  136. if (blocked.TryGetValue(node, out blockersInNode)) {
  137. blockersInNode.Remove(blocker);
  138. if (blockersInNode.Count == 0) {
  139. blocked.Remove(node);
  140. ListPool<SingleNodeBlocker>.Release(ref blockersInNode);
  141. }
  142. }
  143. }));
  144. }
  145. }
  146. }