import { _decorator, Component, Vec2 } from 'cc'; const { ccclass, property } = _decorator; // A*寻路节点 export class PathNode { x: number; y: number; gCost: number = 0; // 从起点到当前点的实际代价 hCost: number = 0; // 从当前点到终点的启发式代价 fCost: number = 0; // 总代价 f = g + h parent: PathNode | null = null; walkable: boolean = true; moveCost: number = 1.0; // 从父节点移动到此节点的代价 constructor(x: number, y: number, walkable: boolean = true) { this.x = x; this.y = y; this.walkable = walkable; } calculateFCost() { this.fCost = this.gCost + this.hCost; } } @ccclass('AStarPathfinding') export class AStarPathfinding extends Component { private grid: PathNode[][] = []; private gridWidth: number = 0; private gridHeight: number = 0; /** * 初始化寻路网格 * @param width 网格宽度 * @param height 网格高度 * @param walkableData 可行走数据,0表示不可行走,1表示可行走 */ initializeGrid(width: number, height: number, walkableData: number[][]) { this.gridWidth = width; this.gridHeight = height; this.grid = []; for (let x = 0; x < width; x++) { this.grid[x] = []; for (let y = 0; y < height; y++) { const walkable = walkableData[y] && walkableData[y][x] === 1; this.grid[x][y] = new PathNode(x, y, walkable); } } } /** * 使用A*算法寻找路径 * @param startX 起点X坐标 * @param startY 起点Y坐标 * @param targetX 终点X坐标 * @param targetY 终点Y坐标 * @returns 路径点数组,如果找不到路径返回空数组 */ findPath(startX: number, startY: number, targetX: number, targetY: number): Vec2[] { // 验证起点和终点是否有效 if (!this.isValidPosition(startX, startY) || !this.isValidPosition(targetX, targetY)) { console.warn('起点或终点坐标无效'); return []; } if (!this.grid[startX][startY].walkable || !this.grid[targetX][targetY].walkable) { console.warn('起点或终点不可行走'); return []; } if (startX === targetX && startY === targetY) { return [new Vec2(startX, startY)]; } // 重置所有节点 this.resetNodes(); const startNode = this.grid[startX][startY]; const targetNode = this.grid[targetX][targetY]; const openSet: PathNode[] = []; const closedSet: PathNode[] = []; openSet.push(startNode); while (openSet.length > 0) { // 找到f值最小的节点 let currentNode = openSet[0]; for (let i = 1; i < openSet.length; i++) { if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost === currentNode.fCost && openSet[i].hCost < currentNode.hCost)) { currentNode = openSet[i]; } } // 从开放列表移除当前节点,添加到关闭列表 openSet.splice(openSet.indexOf(currentNode), 1); closedSet.push(currentNode); // 如果到达目标节点,重建路径 if (currentNode === targetNode) { return this.retracePath(startNode, targetNode); } // 检查相邻节点 const neighbors = this.getNeighbors(currentNode); for (const neighbor of neighbors) { if (!neighbor.walkable || closedSet.indexOf(neighbor) !== -1) { continue; } const newGCost = currentNode.gCost + neighbor.moveCost; if (newGCost < neighbor.gCost || openSet.indexOf(neighbor) === -1) { neighbor.gCost = newGCost; neighbor.hCost = this.getDistance(neighbor, targetNode); neighbor.calculateFCost(); neighbor.parent = currentNode; if (openSet.indexOf(neighbor) === -1) { openSet.push(neighbor); } } } } // 没有找到路径 return []; } /** * 获取节点的相邻节点(8方向) */ private getNeighbors(node: PathNode): PathNode[] { const neighbors: PathNode[] = []; // 八个方向:上、下、左、右、左上、右上、左下、右下 const directions = [ { x: 0, y: 1, cost: 1.0 }, // 上 { x: 0, y: -1, cost: 1.0 }, // 下 { x: -1, y: 0, cost: 1.0 }, // 左 { x: 1, y: 0, cost: 1.0 }, // 右 { x: -1, y: 1, cost: 1.414 }, // 左上 { x: 1, y: 1, cost: 1.414 }, // 右上 { x: -1, y: -1, cost: 1.414 },// 左下 { x: 1, y: -1, cost: 1.414 } // 右下 ]; for (const dir of directions) { const checkX = node.x + dir.x; const checkY = node.y + dir.y; if (this.isValidPosition(checkX, checkY)) { const neighbor = this.grid[checkX][checkY]; // 为对角线移动添加额外检查,防止穿过墙角 if (dir.cost > 1.0) { // 检查对角线移动时,相邻的两个直角方向是否可行走 const xCheck = this.grid[node.x + dir.x][node.y]; const yCheck = this.grid[node.x][node.y + dir.y]; if (!xCheck.walkable || !yCheck.walkable) { continue; // 如果相邻的直角方向不可行走,则不能进行对角线移动 } } neighbor.moveCost = dir.cost; neighbors.push(neighbor); } } return neighbors; } /** * 获取两个节点之间的距离(欧几里得距离,支持对角线移动) */ private getDistance(nodeA: PathNode, nodeB: PathNode): number { const distX = Math.abs(nodeA.x - nodeB.x); const distY = Math.abs(nodeA.y - nodeB.y); // 使用欧几里得距离,更适合对角线移动 // 对角线距离约为1.414,直线距离为1 if (distX === distY) { return distX * 1.414; // 对角线移动 } else { return distX + distY; // 曼哈顿距离 } } /** * 重建路径 */ private retracePath(startNode: PathNode, endNode: PathNode): Vec2[] { const path: Vec2[] = []; let currentNode = endNode; while (currentNode !== startNode) { path.push(new Vec2(currentNode.x, currentNode.y)); currentNode = currentNode.parent!; } path.push(new Vec2(startNode.x, startNode.y)); path.reverse(); return path; } /** * 重置所有节点的寻路参数 */ private resetNodes() { for (let x = 0; x < this.gridWidth; x++) { for (let y = 0; y < this.gridHeight; y++) { const node = this.grid[x][y]; node.gCost = 0; node.hCost = 0; node.fCost = 0; node.parent = null; } } } /** * 检查位置是否有效 */ private isValidPosition(x: number, y: number): boolean { return x >= 0 && x < this.gridWidth && y >= 0 && y < this.gridHeight; } /** * 设置节点的可行走状态 */ setWalkable(x: number, y: number, walkable: boolean) { if (this.isValidPosition(x, y)) { this.grid[x][y].walkable = walkable; } } /** * 获取节点是否可行走 */ isWalkable(x: number, y: number): boolean { if (!this.isValidPosition(x, y)) { return false; } return this.grid[x][y].walkable; } }