AStar.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import { Component, Size, _decorator } from "cc";
  2. const { ccclass, property } = _decorator;
  3. export enum Direction {
  4. // 二进制,位操作,比较方便
  5. UP = 1,
  6. DOWN = 1 << 1,
  7. LEFT = 1 << 2,
  8. RIGHT = 1 << 3
  9. }
  10. export class Point {
  11. x: number;
  12. y: number;
  13. // 记录可行走的方向
  14. state: number;
  15. // 默认上下左右都可以
  16. constructor(x: number, y: number, state = Direction.UP | Direction.DOWN | Direction.LEFT | Direction.RIGHT) {
  17. this.x = x;
  18. this.y = y;
  19. this.state = state
  20. }
  21. G: number = 0; //G = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。
  22. H: number = 0; //H = 从网格上那个方格移动到终点B的预估移动耗费
  23. F: number = 0; //F = G + H
  24. // 通行的方向性,有的点可能时单向通行
  25. father: Point = null; //这个点的上一个点,通过回溯可以找到起点
  26. is_close: boolean = false; //是否关闭搜索
  27. //减去某些方向
  28. reduce(direction: number) {
  29. this.state = this.state & (~direction);
  30. return this;
  31. }
  32. // 加上某些方向
  33. add(direction: number) {
  34. this.state = this.state | direction;
  35. return this;
  36. }
  37. // 某方向是否可通行
  38. isPass(direction: number): boolean {
  39. return (this.state & direction) == direction;
  40. }
  41. copy() {
  42. let point = new Point(this.x, this.y, this.state);
  43. for (const propName in this) {
  44. point['' + propName] = this[propName]
  45. }
  46. return point;
  47. }
  48. }
  49. @ccclass
  50. export default class AStar extends Component {
  51. static start: Point = null; //起点
  52. static end: Point = null; //终点
  53. static map: Map<number, Point> = null; //地图point
  54. static size: Size = null; //地图尺寸
  55. static arr_open: Array<Point> = []; //开放队列
  56. static pppp: Point = null; //执行完寻路,它就有值了,除非没找到
  57. /**
  58. * 获取路线 (此寻路不走斜线)
  59. */
  60. static getRoute(start: Point, end: Point, map: Map<number, Point>, size: Size = new Size(1, 1)) {
  61. //清空上次寻路,并赋值
  62. this.is_find = false;
  63. this.arr_open = [];
  64. this.pppp = null;
  65. this.start = start.copy();
  66. this.end = end.copy();
  67. this.map = new Map<number, Point>();
  68. map.forEach((value, key) => {
  69. this.map.set(key, value.copy()); //map 里放的是传过来的对象,使用深拷贝
  70. });
  71. this.size = size;
  72. map.get(this.start.x + this.start.y * this.size.width).G = 0; //起点的G是0
  73. //开始寻路
  74. let route = new Array<Point>();
  75. try {
  76. this.search(this.start); //内存不够会报错,一般是起点或终点封闭
  77. } catch (error) {
  78. console.error("位置不对");
  79. return route;
  80. }
  81. if (this.pppp) {
  82. this.getFather(this.pppp, route);
  83. }
  84. return route;
  85. }
  86. /**
  87. * 多Point寻路,多个不同起点寻找最优起点,的最优路线,statrs的优先收索权重应该给出,并且starts运算完成后会有修改
  88. */
  89. static getRouteByStars(starts: Point[], end: Point, map: Map<number, Point>, size: Size = new Size(1, 1)) {
  90. //清空上次寻路,并赋值
  91. this.is_find = false;
  92. this.arr_open = [];
  93. this.pppp = null;
  94. this.end = end.copy();
  95. this.map = new Map<number, Point>();
  96. map.forEach((value, key) => {
  97. this.map.set(key, value.copy()); //map 里放的是传过来的对象,使用深拷贝
  98. });
  99. this.size = size;
  100. //开始寻路
  101. let route = new Array<Point>();
  102. try {
  103. let arr = starts;
  104. arr.forEach(p => {
  105. this.setFather(p, p);
  106. p.father = null
  107. });
  108. //arr按照F排序 从小到大
  109. this.arr_open = arr;
  110. this.arr_open.sort(this.compare);
  111. for(let i =0;i<this.arr_open.length;i++){
  112. let pp = this.arr_open[i];
  113. if (pp.is_close) { //删除没用的
  114. this.arr_open.splice(i, 1);
  115. i--; //重置索引,或者倒序
  116. } else if (!this.is_find) {
  117. this.search(pp);
  118. }
  119. }
  120. } catch (error) {
  121. console.error("位置不对");
  122. return route;
  123. }
  124. if (this.pppp) {
  125. this.getFather(this.pppp, route);
  126. }
  127. return route;
  128. }
  129. /**
  130. * 寻路
  131. */
  132. static is_find = false; //是否已经找到路线
  133. static search(point: Point) {
  134. if (point.x == this.end.x && point.y == this.end.y) {
  135. this.is_find = true;
  136. this.pppp = point;
  137. return;
  138. }
  139. let arr = this.getAround(point);
  140. arr.forEach(p => {
  141. this.setFather(p, point);
  142. });
  143. //arr按照F排序 从小到大
  144. this.arr_open.sort(this.compare);
  145. //递归继续找
  146. for(let i =0;i<this.arr_open.length;i++){
  147. let pp = this.arr_open[i];
  148. if (pp.is_close) { //删除没用的
  149. this.arr_open.splice(i, 1);
  150. i--;
  151. } else if (!this.is_find) {
  152. this.search(pp);
  153. }
  154. }
  155. }
  156. /**
  157. * 获取周围4个点,上下左右
  158. */
  159. static getAround(point: Point) {
  160. point.is_close = true;
  161. let arr = new Array<Point>();
  162. let index: number;
  163. let p: Point;
  164. // 这里上下是反向的, 以y轴以上是下 ,
  165. //上
  166. if (point.y != 0 && point.isPass(Direction.DOWN)) { //到顶了,没有上
  167. index = point.x + (point.y - 1) * this.size.width;
  168. p = this.map.get(index)
  169. if (p && !p.is_close) {
  170. arr.push(this.map.get(index));
  171. this.arr_open.push(this.map.get(index)); //我也要一份
  172. }
  173. }
  174. //下
  175. if (point.y + 1 != this.size.height && point.isPass(Direction.UP)) { //到底了,没有下
  176. index = point.x + (point.y + 1) * this.size.width;
  177. p = this.map.get(index)
  178. if (p && !p.is_close) {
  179. arr.push(this.map.get(index));
  180. this.arr_open.push(this.map.get(index));
  181. }
  182. }
  183. //左
  184. if (point.x != 0 && point.isPass(Direction.LEFT)) { //同理
  185. index = point.x - 1 + point.y * this.size.width;
  186. p = this.map.get(index)
  187. if (p && !p.is_close) {
  188. arr.push(this.map.get(index));
  189. this.arr_open.push(this.map.get(index));
  190. }
  191. }
  192. //右
  193. if (point.x + 1 != this.size.width && point.isPass(Direction.RIGHT)) { //同理
  194. index = point.x + 1 + point.y * this.size.width;
  195. p = this.map.get(index)
  196. if (p && !p.is_close) {
  197. arr.push(this.map.get(index));
  198. this.arr_open.push(this.map.get(index));
  199. }
  200. }
  201. return arr;
  202. }
  203. /**
  204. * point换父亲,并重新计算G、H、F
  205. */
  206. static setFather(son: Point, father: Point) {
  207. if (!son.father || son.father.G > father.G) {
  208. son.father = father;
  209. son.G = son.father.G + 1;
  210. son.H = Math.abs(son.x - this.end.x) + Math.abs(son.y - this.end.y);
  211. son.F = son.G + son.H;
  212. }
  213. }
  214. /**
  215. * 比较器
  216. */
  217. static compare(p1: Point, p2: Point) {
  218. if (p1.F > p2.F) {
  219. return 1;
  220. } else {
  221. return -1;
  222. }
  223. }
  224. /**
  225. * 递归 把祖宗放进route里面
  226. */
  227. static getFather(point: Point, route: Array<Point>) {
  228. let father = point.father;
  229. if (father) {
  230. this.getFather(father, route);
  231. }
  232. route.push(point);
  233. }
  234. }