feat(pathfinding): 支持从不可行走位置开始寻路

改进寻路系统,允许玩家从不可行走的当前位置开始寻路到可行走区域。
当玩家位于不可行走位置时,系统会自动寻找最近的可行走位置作为起点,
并临时将起点设置为可行走状态以启动A*算法。

主要变更:
- AStarPathfinding: 临时修改起点可行走状态以支持算法启动
- PlayerController: 检测玩家当前位置并自动传送到最近可行走点
- TiledMapPathfinder: 在寻路前验证起点并寻找替代位置
This commit is contained in:
richarjiang
2025-10-20 09:23:04 +08:00
parent 8f4200a7a3
commit 972334f786
3 changed files with 46 additions and 4 deletions

View File

@@ -65,11 +65,15 @@ export class AStarPathfinding extends Component {
return []; return [];
} }
if (!this.grid[startX][startY].walkable || !this.grid[targetX][targetY].walkable) { // 检查终点是否可行走
console.warn('起点或终点不可行走'); if (!this.grid[targetX][targetY].walkable) {
console.warn('终点不可行走');
return []; return [];
} }
// 注意起点可能不可行走我们在TiledMapPathfinder中已经处理了这种情况
// 这里不再检查起点是否可行走,允许从不可行走的起点开始寻路
if (startX === targetX && startY === targetY) { if (startX === targetX && startY === targetY) {
return [new Vec2(startX, startY)]; return [new Vec2(startX, startY)];
} }
@@ -80,6 +84,10 @@ export class AStarPathfinding extends Component {
const startNode = this.grid[startX][startY]; const startNode = this.grid[startX][startY];
const targetNode = this.grid[targetX][targetY]; const targetNode = this.grid[targetX][targetY];
// 临时将起点设置为可行走,以便算法能够开始
const originalStartWalkable = startNode.walkable;
startNode.walkable = true;
const openSet: PathNode[] = []; const openSet: PathNode[] = [];
const closedSet: PathNode[] = []; const closedSet: PathNode[] = [];
@@ -101,12 +109,16 @@ export class AStarPathfinding extends Component {
// 如果到达目标节点,重建路径 // 如果到达目标节点,重建路径
if (currentNode === targetNode) { if (currentNode === targetNode) {
// 恢复起点的原始可行走状态
startNode.walkable = originalStartWalkable;
return this.retracePath(startNode, targetNode); return this.retracePath(startNode, targetNode);
} }
// 检查相邻节点 // 检查相邻节点
const neighbors = this.getNeighbors(currentNode); const neighbors = this.getNeighbors(currentNode);
for (const neighbor of neighbors) { for (const neighbor of neighbors) {
// 如果当前节点不可行走,我们仍然允许它连接到可行走的相邻节点
// 这样可以从不可行走的起点移动到可行走的区域
if (!neighbor.walkable || closedSet.indexOf(neighbor) !== -1) { if (!neighbor.walkable || closedSet.indexOf(neighbor) !== -1) {
continue; continue;
} }
@@ -126,6 +138,9 @@ export class AStarPathfinding extends Component {
} }
} }
// 恢复起点的原始可行走状态
startNode.walkable = originalStartWalkable;
// 没有找到路径 // 没有找到路径
return []; return [];
} }

View File

@@ -238,8 +238,23 @@ export class PlayerController extends Component {
clampedPos.set(closestWalkable); clampedPos.set(closestWalkable);
} }
// 检查玩家当前位置是否可行走
let startPos = this.player.position;
if (!this.pathfinder.isWorldPositionWalkable(startPos)) {
console.log('玩家当前位置不可行走,寻找最近的可行走位置作为起点');
const closestPlayerWalkable = this.pathfinder.getClosestWalkablePosition(startPos);
if (!closestPlayerWalkable) {
console.warn('找不到玩家附近的可行走位置');
return;
}
startPos = closestPlayerWalkable;
console.log(`将玩家移动到最近的可行走位置: (${startPos.x.toFixed(2)}, ${startPos.y.toFixed(2)})`);
// 直接将玩家传送到最近的可行走位置
this.player.setPosition(startPos);
}
// 使用寻路算法计算路径 // 使用寻路算法计算路径
const startPos = this.player.position;
this.currentPath = this.pathfinder.findPath(startPos, clampedPos); this.currentPath = this.pathfinder.findPath(startPos, clampedPos);
if (this.currentPath.length === 0) { if (this.currentPath.length === 0) {

View File

@@ -111,11 +111,23 @@ export class TiledMapPathfinder extends Component {
} }
// 将世界坐标转换为瓦片坐标 // 将世界坐标转换为瓦片坐标
const startTilePos = this.worldToTileCoordinate(startWorldPos); let startTilePos = this.worldToTileCoordinate(startWorldPos);
const targetTilePos = this.worldToTileCoordinate(targetWorldPos); const targetTilePos = this.worldToTileCoordinate(targetWorldPos);
console.log(`寻路: 起点瓦片坐标(${startTilePos.x}, ${startTilePos.y}) -> 终点瓦片坐标(${targetTilePos.x}, ${targetTilePos.y})`); console.log(`寻路: 起点瓦片坐标(${startTilePos.x}, ${startTilePos.y}) -> 终点瓦片坐标(${targetTilePos.x}, ${targetTilePos.y})`);
// 检查起点是否可行走,如果不可行走则寻找最近的可行走位置
if (!this.pathfinding.isWalkable(startTilePos.x, startTilePos.y)) {
console.log('起点不可行走,寻找最近的可行走位置');
const closestWalkableWorldPos = this.getClosestWalkablePosition(startWorldPos);
if (!closestWalkableWorldPos) {
console.warn('找不到起点附近的可行走位置');
return [];
}
startTilePos = this.worldToTileCoordinate(closestWalkableWorldPos);
console.log(`使用新的起点瓦片坐标(${startTilePos.x}, ${startTilePos.y})`);
}
// 使用A*算法寻找路径 // 使用A*算法寻找路径
const tilePath = this.pathfinding.findPath( const tilePath = this.pathfinding.findPath(
startTilePos.x, startTilePos.y, startTilePos.x, startTilePos.y,