feat: 支持自动寻路算法
This commit is contained in:
225
assets/scripts/AStarPathfinding.ts
Normal file
225
assets/scripts/AStarPathfinding.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
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;
|
||||
|
||||
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 + this.getDistance(currentNode, neighbor);
|
||||
|
||||
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 [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点的相邻节点(4方向)
|
||||
*/
|
||||
private getNeighbors(node: PathNode): PathNode[] {
|
||||
const neighbors: PathNode[] = [];
|
||||
|
||||
// 四个方向:上、下、左、右
|
||||
const directions = [
|
||||
{ x: 0, y: 1 }, // 上
|
||||
{ x: 0, y: -1 }, // 下
|
||||
{ x: -1, y: 0 }, // 左
|
||||
{ x: 1, y: 0 } // 右
|
||||
];
|
||||
|
||||
for (const dir of directions) {
|
||||
const checkX = node.x + dir.x;
|
||||
const checkY = node.y + dir.y;
|
||||
|
||||
if (this.isValidPosition(checkX, checkY)) {
|
||||
neighbors.push(this.grid[checkX][checkY]);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user