AStarManager.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. class AStarNode {
  2. private _x:number = 0;
  3. private _y:number = 0;
  4. get x() {return this._x;}
  5. get y() {return this._y;}
  6. /**
  7. * @param x 节点横坐标
  8. * @param y 节点纵坐标
  9. */
  10. constructor(x:number, y:number) {
  11. this._x = x;
  12. this._y = y;
  13. }
  14. priority = 0;
  15. }
  16. class Frontier {
  17. private arr:AStarNode[] = [];//创建字典(数组)
  18. put(node:AStarNode, priority:number) {
  19. node.priority = priority;
  20. this.arr.push(node); //添加元素
  21. this.arr.sort((a,b)=>b.priority - a.priority);
  22. }
  23. get() {
  24. return this.arr.pop();//删除元素
  25. }
  26. get size() {
  27. return this.arr.length;
  28. }
  29. reset(){
  30. this.arr = [];
  31. }
  32. }
  33. export default class AStarManager{
  34. private static instance:AStarManager = null;
  35. static Instance() {
  36. if (!this.instance) {
  37. this.instance = new AStarManager();
  38. }
  39. return this.instance;
  40. };
  41. // 通过private修饰符,让类无法在外部创建新的实例
  42. private constructor() {
  43. }
  44. private mSize:cc.Size = null; // 寻路地图大小
  45. private mStart:cc.Vec2 = null; // 寻路起始点坐标
  46. private mEnd:cc.Vec2 = null; // 寻路目标点坐标
  47. private mStartNode:AStarNode = null; // 起始点
  48. private mEndNode:AStarNode = null; // 目标点
  49. /**
  50. * 设置地图横纵最大值
  51. * @param size 地图大小
  52. * @param start 寻路起始点
  53. * @param end 寻路目标点
  54. * @param obstacles 障碍物
  55. */
  56. public init(size:cc.Size, start:cc.Vec2, end:cc.Vec2, obstacles:cc.Vec2[] = []) {
  57. this.mSize = size;
  58. this.mStart = start;
  59. this.mEnd = end;
  60. this.obstacles = {};
  61. obstacles.forEach((ele) => {
  62. this.setObstacles(ele.x, ele.y);
  63. });
  64. this.nodePool = {};
  65. this.mStartNode = this.createNode(this.mStart.x, this.mStart.y);
  66. this.mEndNode = this.createNode(this.mEnd.x, this.mEnd.y);
  67. }
  68. public clean(){
  69. this.mStartNode = null;
  70. this.mEndNode = null;
  71. }
  72. private obstacles:{[x_y:string]:boolean} = {};
  73. /**
  74. * 设置障碍物
  75. * @param x 障碍物横坐标
  76. * @param y 障碍物纵坐标
  77. */
  78. public setObstacles(x:number, y:number) {
  79. if (!this.checkNode(x, y)) return;
  80. this.obstacles[`${x}_${y}`] = true;
  81. }
  82. /**
  83. * 清除障碍物
  84. * @param x 障碍物横坐标
  85. * @param y 障碍物纵坐标
  86. */
  87. public clearObstacles(x:number, y:number) {
  88. delete this.obstacles[`${x}_${y}`];
  89. }
  90. /**
  91. * 检查是否有障碍物
  92. * @param x 障碍物横坐标
  93. * @param y 障碍物纵坐标
  94. */
  95. public checkObstacles(x:number, y:number) {
  96. return this.obstacles[`${x}_${y}`];
  97. }
  98. /**
  99. * 检查节点是否在地图内
  100. * @param x 节点横坐标
  101. * @param y 节点纵坐标
  102. */
  103. public checkNode(x:number, y:number) {
  104. return x >= 0 && y >= 0 && x <= this.mSize.width && y <= this.mSize.height;
  105. }
  106. ///////////////////////////// 寻路开始 ////////////////////
  107. // 节点池
  108. private nodePool:{[x_y:string]:AStarNode} = {};
  109. // 探索边界
  110. private frontier:Frontier = null;
  111. // 节点来向(路径)
  112. private cameForm:Map<AStarNode, AStarNode> = new Map();
  113. // 节点当前代价
  114. private costSoFar:Map<AStarNode, number> = new Map();
  115. // 寻路类型(4方向或8方向)
  116. private mType: 4|8;
  117. private closeList:string[] = [];
  118. /**
  119. * 开始寻路
  120. * @param type 寻路类型,4方向或8方向
  121. */
  122. public run(type:4|8 = 4) {
  123. this.mType = type;
  124. // console.log('##### 寻路开始 #####');
  125. // console.log('地图大小:', this.mSize);
  126. // console.log('寻路类型:', this.mType + '方向');
  127. // console.log(`出发点X:${this.mStart.x}_Y:${this.mStart.y}`);
  128. // console.log(`目标点:X:${this.mEnd.x}_Y:${this.mEnd.y}`);
  129. // console.log('障碍物:', this.obstacles);
  130. const start = this.mStartNode;
  131. const goal = this.mEndNode;
  132. if (this.frontier)
  133. this.frontier.reset();
  134. else
  135. this.frontier = new Frontier();
  136. this.frontier.put(start, 0);//将起始点加入openList,表示我们从这里触发,是我们第一步可以走的点
  137. this.cameForm.clear();
  138. this.cameForm.set(start, null);
  139. this.costSoFar.clear();
  140. this.costSoFar.set(start, 0);
  141. this.closeList = [];
  142. while (this.frontier.size > 0) {
  143. let curr = this.frontier.get(); // 每次都取出f值最小的节点进行查找
  144. if (curr === goal) {
  145. break;
  146. }
  147. let nextList = this.getNeighbors(curr); //从openList中取出一个点作为nextNode,获取这个点下一步所有可走的点,存入nextList
  148. for (let next of nextList) {//如果nextList长度为0,说明没有路可走了;长度为大于0,则将nextList中的点都放入openList
  149. if(this.closeList.indexOf(this.getNodeKey(next.x,next.y)) < 0){
  150. const nextCost = this.costSoFar.get(curr) + this.getCost(curr, next);
  151. // console.log("next的索引:"+this.getNodeKey(next.x,next.y)+",开销:"+nextCost+"父节点索引:"+this.getNodeKey(curr.x,curr.y))
  152. if (!this.costSoFar.has(next) || nextCost < this.costSoFar.get(next)) {
  153. this.costSoFar.set(next, nextCost);
  154. const preCost = this.getCost(next, goal);
  155. this.frontier.put(next, nextCost + preCost);//内部根据开销进行了自动排序(从小到大)
  156. this.cameForm.set(next, curr);
  157. }
  158. }
  159. }
  160. this.closeList.push(this.getNodeKey(curr.x,curr.y));
  161. }
  162. }
  163. public getPath() {
  164. const arr = [];
  165. let node = this.mEndNode;
  166. arr.push(node);
  167. while (this.cameForm.has(node)) {
  168. node = this.cameForm.get(node);
  169. node && arr.push(node);
  170. }
  171. return arr;
  172. }
  173. getNeighbors(node:AStarNode) {
  174. const neighbors = [];
  175. if (this.mType === 8) {
  176. const upright = this.getNode(node.x + 1, node.y + 1);
  177. upright && neighbors.push(upright);
  178. const upleft = this.getNode(node.x - 1, node.y + 1);
  179. upleft && neighbors.push(upleft);
  180. const downleft = this.getNode(node.x - 1, node.y - 1);
  181. downleft && neighbors.push(downleft);
  182. const downright = this.getNode(node.x + 1, node.y - 1);
  183. downright && neighbors.push(downright);
  184. }
  185. const up = this.getNode(node.x, node.y + 1);
  186. up && neighbors.push(up);
  187. const down = this.getNode(node.x, node.y - 1);
  188. down && neighbors.push(down);
  189. const left = this.getNode(node.x - 1, node.y);
  190. left && neighbors.push(left);
  191. const right = this.getNode(node.x + 1, node.y);
  192. right && neighbors.push(right);
  193. return neighbors;
  194. }
  195. getCost(node1:AStarNode, node2:AStarNode) {
  196. return cc.Vec2.distance(new cc.Vec2(node1.x, node1.y), new cc.Vec2(node2.x, node2.y));
  197. }
  198. // 寻找下一个点
  199. public next() {
  200. }
  201. private getNode(x:number, y:number): AStarNode {
  202. if (!this.checkNode(x, y)) return;
  203. if (this.checkObstacles(x, y)) return;
  204. const key = this.getNodeKey(x, y);
  205. return this.nodePool[key] || this.createNode(x, y);
  206. }
  207. private createNode(x:number, y:number) {
  208. const key = this.getNodeKey(x, y);
  209. this.nodePool[key] = new AStarNode(x, y);
  210. return this.nodePool[key];
  211. }
  212. private getNodeKey(x:number, y:number) {
  213. return `${x}_${y}`;
  214. }
  215. }