import { Size, TiledMap, UITransform, v3, Vec2 } from "cc"; import { BinaryHeap } from "./BinaryHeap"; export namespace AStar { //寻路方向 export enum Type { error = 0, four, //4个方向 eight, //8个方向 } //寻路节点 export class Node { x: number = 0; y: number = 0; f: number = 0; //总消耗 g: number = 0; //起点到当前点的消耗 h: number = 0; //当前点到终点的距离 close: boolean = false; //是否被访问过 prev: Node = null; //该节点的上个节点 idx: number = 0; //在地图中的下标 open: boolean = false; //是否是在开放列表中 constructor(x: number, y: number, idx: number) { this.x = x; this.y = y; this.idx = idx; } clear() { this.f = this.g = this.h = 0; this.open = this.close = false; this.prev = null; } } const oblique_cost = 1.25; //斜走消耗 const straight_cost = 1; //直走消耗 export class Path { private _node_list = new Map(); //节点列表 private _found_node: Node = null; //找到的节点(递归用) private _map_size: Size = null; //地图尺寸 private _tile_size: Size = null; //地图块尺寸 private _start_node = new Node(0, 0, 0); //起点节点 private _end_node = new Node(0, 0, 0); //终点节点 private _path_type = Type.four; //寻路方向 private _find_path: Node[] = []; //路径列表(tiledmap坐标) private _min_heap = new BinaryHeap((node: Node) => { return node.f });//开放列表二叉堆 private _tiled_map: TiledMap = null; /** * 寻路初始化函数 * @param nodes 地图数据(只包含可走的节点) * @param map 地图 * @param type 寻路方向 */ constructor(nodes: Map, map: TiledMap, type: Type = Type.four) { this._node_list = nodes; this._tiled_map = map; this._map_size = this._tiled_map.getMapSize(); this._tile_size = this._tiled_map.getTileSize(); this._path_type = type; } /** * 搜索路径 * @param start 起点(tiledmap坐标) * @param end 终点(tiledmap坐标) */ async search(start: Vec2, end: Vec2): Promise { //清理路径节点 this._node_list.forEach((value) => { value.clear(); }); //清理列表 this._min_heap.clear(); this._find_path = []; this._start_node.x = start.x; this._start_node.y = start.y; this._start_node.idx = this._start_node.x + this._start_node.y * this._map_size.width; this._start_node.clear(); this._end_node.x = end.x; this._end_node.y = end.y; this._end_node.idx = this._end_node.x + this._end_node.y * this._map_size.width; this._end_node.clear(); //开始寻路 this._search(this._start_node); //删除起点 this._find_path.shift(); return this._find_path; } private _search(start: Node) { if (start.x == this._end_node.x && start.y == this._end_node.y) { this._found_node = start; this._getPrev(this._found_node); return true; } this._getRoundNodes(start); let node = this._min_heap.root(); (node) && this._search(node); } //依次获取上个节点 private _getPrev(node: Node) { if (node.prev) this._getPrev(node.prev); this._find_path.push(node); } //获取周围的节点 private _getRoundNodes(current: Node) { current.close = true; current.open = false; //四个方向 //上 if (current.y != 0) { this._addToHeap(current.idx - this._map_size.width, current, straight_cost); } //右 if (current.x != this._map_size.width - 1) { this._addToHeap(current.idx + 1, current, straight_cost); } //下 if (current.y != this._map_size.height - 1) { this._addToHeap(current.idx + this._map_size.width, current, straight_cost); } //左 if (current.x != 0) { this._addToHeap(current.idx - 1, current, straight_cost); } if (this._path_type == Type.eight) { //右上 if (current.y != 0 && current.x != this._map_size.width - 1) { this._addToHeap(current.idx - this._map_size.width + 1, current, oblique_cost); } //右下 if (current.x != this._map_size.width - 1 && current.y != this._map_size.height - 1) { this._addToHeap(current.idx + this._map_size.width + 1, current, oblique_cost); } //左下 if (current.x != 0 && current.y != this._map_size.height - 1) { this._addToHeap(current.idx + this._map_size.width - 1, current, oblique_cost); } //左上 if (current.x != 0 && current.y != 0) { this._addToHeap(current.idx - this._map_size.width - 1, current, oblique_cost); } } } private _addToHeap(idx: number, current: Node, cost: number) { let node = this._node_list.get(idx); if (node && !node.close) { //如果在开放列表中则从表中移除 (node.open) && this._min_heap.remove(node); //更新权重 node.prev = current; node.g = current.g + cost; node.h = Math.abs(node.x - this._end_node.x) + Math.abs(node.y - this._end_node.y); node.f = node.g + node.h; node.open = true; this._min_heap.insert(node); } } } }