GridPathfinding.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using Unity.Mathematics;
  5. using Unity.Collections;
  6. using Unity.Jobs;
  7. using Unity.Burst;
  8. using KairoEngine.Core;
  9. public class GridPathfinding : MonoBehaviour {
  10. private const int MOVE_STRAIGHT_COST = 10;
  11. private const int MOVE_DIAGONAL_COST = 14;
  12. private int2 gridSize;
  13. private NativeArray<int> mapArray;
  14. private void Start() {
  15. gridSize = new int2(32, 32);
  16. mapArray = new NativeArray<int>(gridSize.x * gridSize.y, Allocator.Persistent);
  17. for (int x = 0; x < gridSize.x; x++) {
  18. for (int y = 0; y < gridSize.y; y++) {
  19. mapArray[x + y * gridSize.x] = 1;
  20. }
  21. }
  22. Timer.ExecuteRealTime(3000, () => {Loop(); });
  23. }
  24. private void OnDestroy()
  25. {
  26. mapArray.Dispose();
  27. }
  28. private void Loop()
  29. {
  30. Timer.ExecuteRealTime(2000, () => {
  31. float startTime = Time.realtimeSinceStartup;
  32. int findPathJobCount = 200;
  33. NativeArray<JobHandle> jobHandleArray = new NativeArray<JobHandle>(findPathJobCount, Allocator.TempJob);
  34. List<FindPathJob> jobsArray = new List<FindPathJob>();
  35. int2 startPosition = new int2(0, 0);
  36. int2 endPosition = new int2(31, 31);
  37. for (int i = 0; i < findPathJobCount; i++) {
  38. FindPathJob findPathJob = new FindPathJob {
  39. startPosition = startPosition,
  40. endPosition = endPosition,
  41. gridSize = gridSize,
  42. pathFound = new NativeArray<int>(2, Allocator.TempJob),
  43. mapArray = mapArray
  44. };
  45. jobHandleArray[i] = findPathJob.Schedule();
  46. jobsArray.Add(findPathJob);
  47. }
  48. JobHandle.CompleteAll(jobHandleArray);
  49. for (int i = 0; i < jobsArray.Count; i++)
  50. {
  51. bool pathFound = jobsArray[i].pathFound[0] == 1 ? true : false;
  52. Debug.Log($"Path found: {pathFound} - {jobsArray[i].pathFound[1]} points");
  53. jobsArray[i].pathFound.Dispose();
  54. }
  55. jobHandleArray.Dispose();
  56. jobsArray.Clear();
  57. Debug.Log("Time: " + ((Time.realtimeSinceStartup - startTime) * 1000f));
  58. Loop();
  59. });
  60. }
  61. [BurstCompile]
  62. public struct FindPathJob : IJob {
  63. public int2 startPosition;
  64. public int2 endPosition;
  65. public int2 gridSize;
  66. public NativeArray<int> pathFound;
  67. [ReadOnly] public NativeArray<int> mapArray;
  68. public void Execute() {
  69. NativeArray<PathNode> pathNodeArray = new NativeArray<PathNode>(gridSize.x * gridSize.y, Allocator.Temp);
  70. for (int x = 0; x < gridSize.x; x++) {
  71. for (int y = 0; y < gridSize.y; y++) {
  72. int index = CalculateIndex(x, y, gridSize.x);
  73. PathNode pathNode = new PathNode();
  74. pathNode.x = x;
  75. pathNode.y = y;
  76. pathNode.index = index;
  77. pathNode.gCost = int.MaxValue;
  78. pathNode.hCost = CalculateDistanceCost(new int2(x, y), endPosition);
  79. pathNode.CalculateFCost();
  80. pathNode.isWalkable = mapArray[index] == 1 ? true : false;
  81. pathNode.cameFromNodeIndex = -1;
  82. pathNodeArray[pathNode.index] = pathNode;
  83. }
  84. }
  85. NativeArray<int2> neighbourOffsetArray = new NativeArray<int2>(8, Allocator.Temp);
  86. neighbourOffsetArray[0] = new int2(-1, 0); // Left
  87. neighbourOffsetArray[1] = new int2(+1, 0); // Right
  88. neighbourOffsetArray[2] = new int2(0, +1); // Up
  89. neighbourOffsetArray[3] = new int2(0, -1); // Down
  90. neighbourOffsetArray[4] = new int2(-1, -1); // Left Down
  91. neighbourOffsetArray[5] = new int2(-1, +1); // Left Up
  92. neighbourOffsetArray[6] = new int2(+1, -1); // Right Down
  93. neighbourOffsetArray[7] = new int2(+1, +1); // Right Up
  94. int endNodeIndex = CalculateIndex(endPosition.x, endPosition.y, gridSize.x);
  95. PathNode startNode = pathNodeArray[CalculateIndex(startPosition.x, startPosition.y, gridSize.x)];
  96. startNode.gCost = 0;
  97. startNode.CalculateFCost();
  98. pathNodeArray[startNode.index] = startNode;
  99. NativeList<int> openList = new NativeList<int>(Allocator.Temp);
  100. NativeList<int> closedList = new NativeList<int>(Allocator.Temp);
  101. openList.Add(startNode.index);
  102. while (openList.Length > 0) {
  103. int currentNodeIndex = GetLowestCostFNodeIndex(openList, pathNodeArray);
  104. PathNode currentNode = pathNodeArray[currentNodeIndex];
  105. if (currentNodeIndex == endNodeIndex) {
  106. // Reached our destination!
  107. break;
  108. }
  109. // Remove current node from Open List
  110. for (int i = 0; i < openList.Length; i++) {
  111. if (openList[i] == currentNodeIndex) {
  112. openList.RemoveAtSwapBack(i);
  113. break;
  114. }
  115. }
  116. closedList.Add(currentNodeIndex);
  117. for (int i = 0; i < neighbourOffsetArray.Length; i++) {
  118. int2 neighbourOffset = neighbourOffsetArray[i];
  119. int2 neighbourPosition = new int2(currentNode.x + neighbourOffset.x, currentNode.y + neighbourOffset.y);
  120. if (!IsPositionInsideGrid(neighbourPosition, gridSize)) {
  121. // Neighbour not valid position
  122. continue;
  123. }
  124. int neighbourNodeIndex = CalculateIndex(neighbourPosition.x, neighbourPosition.y, gridSize.x);
  125. if (closedList.Contains(neighbourNodeIndex)) {
  126. // Already searched this node
  127. continue;
  128. }
  129. PathNode neighbourNode = pathNodeArray[neighbourNodeIndex];
  130. if (!neighbourNode.isWalkable) {
  131. // Not walkable
  132. continue;
  133. }
  134. int2 currentNodePosition = new int2(currentNode.x, currentNode.y);
  135. int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNodePosition, neighbourPosition);
  136. if (tentativeGCost < neighbourNode.gCost) {
  137. neighbourNode.cameFromNodeIndex = currentNodeIndex;
  138. neighbourNode.gCost = tentativeGCost;
  139. neighbourNode.CalculateFCost();
  140. pathNodeArray[neighbourNodeIndex] = neighbourNode;
  141. if (!openList.Contains(neighbourNode.index)) {
  142. openList.Add(neighbourNode.index);
  143. }
  144. }
  145. }
  146. }
  147. PathNode endNode = pathNodeArray[endNodeIndex];
  148. if (endNode.cameFromNodeIndex == -1) {
  149. // Didn't find a path!
  150. //Debug.Log("Didn't find a path!");
  151. pathFound[0] = 0;
  152. pathFound[1] = 0;
  153. } else {
  154. // Found a path
  155. pathFound[0] = 1;
  156. NativeList<int2> path = CalculatePath(pathNodeArray, endNode);
  157. pathFound[1] = path.Length;
  158. /*
  159. foreach (int2 pathPosition in path) {
  160. Debug.Log(pathPosition);
  161. }
  162. */
  163. path.Dispose();
  164. }
  165. //pathNodeArray.Dispose();
  166. neighbourOffsetArray.Dispose();
  167. openList.Dispose();
  168. closedList.Dispose();
  169. }
  170. public NativeList<int2> CalculatePath(NativeArray<PathNode> pathNodeArray, PathNode endNode) {
  171. if (endNode.cameFromNodeIndex == -1) {
  172. // Couldn't find a path!
  173. return new NativeList<int2>(Allocator.Temp);
  174. } else {
  175. // Found a path
  176. NativeList<int2> path = new NativeList<int2>(Allocator.Temp);
  177. path.Add(new int2(endNode.x, endNode.y));
  178. PathNode currentNode = endNode;
  179. while (currentNode.cameFromNodeIndex != -1) {
  180. PathNode cameFromNode = pathNodeArray[currentNode.cameFromNodeIndex];
  181. path.Add(new int2(cameFromNode.x, cameFromNode.y));
  182. currentNode = cameFromNode;
  183. }
  184. return path;
  185. }
  186. }
  187. public bool IsPositionInsideGrid(int2 gridPosition, int2 gridSize) {
  188. return
  189. gridPosition.x >= 0 &&
  190. gridPosition.y >= 0 &&
  191. gridPosition.x < gridSize.x &&
  192. gridPosition.y < gridSize.y;
  193. }
  194. public int CalculateIndex(int x, int y, int gridWidth) {
  195. return x + y * gridWidth;
  196. }
  197. public int CalculateDistanceCost(int2 aPosition, int2 bPosition) {
  198. int xDistance = math.abs(aPosition.x - bPosition.x);
  199. int yDistance = math.abs(aPosition.y - bPosition.y);
  200. int remaining = math.abs(xDistance - yDistance);
  201. return MOVE_DIAGONAL_COST * math.min(xDistance, yDistance) + MOVE_STRAIGHT_COST * remaining;
  202. }
  203. public int GetLowestCostFNodeIndex(NativeList<int> openList, NativeArray<PathNode> pathNodeArray) {
  204. PathNode lowestCostPathNode = pathNodeArray[openList[0]];
  205. for (int i = 1; i < openList.Length; i++) {
  206. PathNode testPathNode = pathNodeArray[openList[i]];
  207. if (testPathNode.fCost < lowestCostPathNode.fCost) {
  208. lowestCostPathNode = testPathNode;
  209. }
  210. }
  211. return lowestCostPathNode.index;
  212. }
  213. }
  214. public struct PathNode {
  215. public int x;
  216. public int y;
  217. public int index;
  218. public int gCost;
  219. public int hCost;
  220. public int fCost;
  221. public bool isWalkable;
  222. public int cameFromNodeIndex;
  223. public void CalculateFCost() {
  224. fCost = gCost + hCost;
  225. }
  226. public void SetIsWalkable(bool isWalkable) {
  227. this.isWalkable = isWalkable;
  228. }
  229. }
  230. }