* docs(modules): 添加框架模块文档 添加以下模块的完整文档: - FSM (状态机): 状态定义、转换条件、优先级、事件监听 - Timer (定时器): 定时器调度、冷却系统、服务令牌 - Spatial (空间索引): GridSpatialIndex、AOI 兴趣区域管理 - Pathfinding (寻路): A* 算法、网格地图、导航网格、路径平滑 - Procgen (程序化生成): 噪声函数、种子随机数、加权随机 所有文档均基于实际源码 API 编写,包含: - 快速开始示例 - 完整 API 参考 - 实际使用案例 - 蓝图节点说明 - 最佳实践建议 * docs(modules): 添加 Blueprint 模块文档和所有模块英文版 新增中文文档: - Blueprint (蓝图可视化脚本): VM、自定义节点、组合系统、触发器 新增英文文档 (docs/en/modules/): - FSM: State machine API, transitions, ECS integration - Timer: Timers, cooldowns, service tokens - Spatial: Grid spatial index, AOI management - Pathfinding: A*, grid map, NavMesh, path smoothing - Procgen: Noise functions, seeded random, weighted random - Blueprint: Visual scripting, custom nodes, composition 所有文档均基于实际源码 API 编写。
12 KiB
12 KiB
寻路系统 (Pathfinding)
@esengine/pathfinding 提供了完整的 2D 寻路解决方案,包括 A* 算法、网格地图、导航网格和路径平滑。
安装
npm install @esengine/pathfinding
快速开始
网格地图寻路
import { createGridMap, createAStarPathfinder } from '@esengine/pathfinding';
// 创建 20x20 的网格地图
const grid = createGridMap(20, 20);
// 设置障碍物
grid.setWalkable(5, 5, false);
grid.setWalkable(5, 6, false);
grid.setWalkable(5, 7, false);
// 创建寻路器
const pathfinder = createAStarPathfinder(grid);
// 查找路径
const result = pathfinder.findPath(0, 0, 15, 15);
if (result.found) {
console.log('找到路径!');
console.log('路径点:', result.path);
console.log('总代价:', result.cost);
console.log('搜索节点数:', result.nodesSearched);
}
导航网格寻路
import { createNavMesh } from '@esengine/pathfinding';
// 创建导航网格
const navmesh = createNavMesh();
// 添加多边形区域
navmesh.addPolygon([
{ x: 0, y: 0 }, { x: 10, y: 0 },
{ x: 10, y: 10 }, { x: 0, y: 10 }
]);
navmesh.addPolygon([
{ x: 10, y: 0 }, { x: 20, y: 0 },
{ x: 20, y: 10 }, { x: 10, y: 10 }
]);
// 自动建立连接
navmesh.build();
// 寻路
const result = navmesh.findPath(1, 1, 18, 8);
核心概念
IPoint - 坐标点
interface IPoint {
readonly x: number;
readonly y: number;
}
IPathResult - 寻路结果
interface IPathResult {
readonly found: boolean; // 是否找到路径
readonly path: readonly IPoint[]; // 路径点列表
readonly cost: number; // 路径总代价
readonly nodesSearched: number; // 搜索的节点数
}
IPathfindingOptions - 寻路配置
interface IPathfindingOptions {
maxNodes?: number; // 最大搜索节点数(默认 10000)
heuristicWeight?: number; // 启发式权重(>1 更快但可能非最优)
allowDiagonal?: boolean; // 是否允许对角移动(默认 true)
avoidCorners?: boolean; // 是否避免穿角(默认 true)
}
启发式函数
模块提供了四种启发式函数:
| 函数 | 适用场景 | 说明 |
|---|---|---|
manhattanDistance |
4方向移动 | 曼哈顿距离,只考虑水平/垂直 |
euclideanDistance |
任意方向 | 欧几里得距离,直线距离 |
chebyshevDistance |
8方向移动 | 切比雪夫距离,对角线代价为 1 |
octileDistance |
8方向移动 | 八角距离,对角线代价为 √2(默认) |
import { manhattanDistance, octileDistance } from '@esengine/pathfinding';
// 自定义启发式
const grid = createGridMap(20, 20, {
heuristic: manhattanDistance // 使用曼哈顿距离
});
网格地图 API
createGridMap
function createGridMap(
width: number,
height: number,
options?: IGridMapOptions
): GridMap
配置选项:
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
allowDiagonal |
boolean |
true |
允许对角移动 |
diagonalCost |
number |
√2 |
对角移动代价 |
avoidCorners |
boolean |
true |
避免穿角 |
heuristic |
HeuristicFunction |
octileDistance |
启发式函数 |
地图操作
// 检查/设置可通行性
grid.isWalkable(x, y);
grid.setWalkable(x, y, false);
// 设置移动代价(如沼泽、沙地)
grid.setCost(x, y, 2); // 代价为 2(默认 1)
// 设置矩形区域
grid.setRectWalkable(0, 0, 5, 5, false);
// 从数组加载(0=可通行,非0=障碍)
grid.loadFromArray([
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 0, 0]
]);
// 从字符串加载(.=可通行,#=障碍)
grid.loadFromString(`
.....
.#.#.
.#...
`);
// 导出为字符串
console.log(grid.toString());
// 重置所有节点为可通行
grid.reset();
方向常量
import { DIRECTIONS_4, DIRECTIONS_8 } from '@esengine/pathfinding';
// 4方向(上下左右)
DIRECTIONS_4 // [{ dx: 0, dy: -1 }, { dx: 1, dy: 0 }, ...]
// 8方向(含对角线)
DIRECTIONS_8 // [{ dx: 0, dy: -1 }, { dx: 1, dy: -1 }, ...]
A* 寻路器 API
createAStarPathfinder
function createAStarPathfinder(map: IPathfindingMap): AStarPathfinder
findPath
const result = pathfinder.findPath(
startX, startY,
endX, endY,
{
maxNodes: 5000, // 限制搜索节点数
heuristicWeight: 1.5 // 加速但可能非最优
}
);
重用寻路器
// 寻路器可重用,内部会自动清理状态
pathfinder.findPath(0, 0, 10, 10);
pathfinder.findPath(5, 5, 15, 15);
// 手动清理(可选)
pathfinder.clear();
导航网格 API
createNavMesh
function createNavMesh(): NavMesh
构建导航网格
const navmesh = createNavMesh();
// 添加凸多边形
const id1 = navmesh.addPolygon([
{ x: 0, y: 0 }, { x: 10, y: 0 },
{ x: 10, y: 10 }, { x: 0, y: 10 }
]);
const id2 = navmesh.addPolygon([
{ x: 10, y: 0 }, { x: 20, y: 0 },
{ x: 20, y: 10 }, { x: 10, y: 10 }
]);
// 方式1:自动检测共享边并建立连接
navmesh.build();
// 方式2:手动设置连接
navmesh.setConnection(id1, id2, {
left: { x: 10, y: 0 },
right: { x: 10, y: 10 }
});
查询和寻路
// 查找包含点的多边形
const polygon = navmesh.findPolygonAt(5, 5);
// 检查位置是否可通行
navmesh.isWalkable(5, 5);
// 寻路(内部使用漏斗算法优化路径)
const result = navmesh.findPath(1, 1, 18, 8);
路径平滑 API
视线简化
移除不必要的中间点:
import { createLineOfSightSmoother } from '@esengine/pathfinding';
const smoother = createLineOfSightSmoother();
const smoothedPath = smoother.smooth(result.path, grid);
// 原路径: [(0,0), (1,1), (2,2), (3,3), (4,4)]
// 简化后: [(0,0), (4,4)]
曲线平滑
使用 Catmull-Rom 样条曲线:
import { createCatmullRomSmoother } from '@esengine/pathfinding';
const smoother = createCatmullRomSmoother(
5, // segments - 每段插值点数
0.5 // tension - 张力 (0-1)
);
const curvedPath = smoother.smooth(result.path, grid);
组合平滑
先简化再曲线平滑:
import { createCombinedSmoother } from '@esengine/pathfinding';
const smoother = createCombinedSmoother(5, 0.5);
const finalPath = smoother.smooth(result.path, grid);
视线检测函数
import { bresenhamLineOfSight, raycastLineOfSight } from '@esengine/pathfinding';
// Bresenham 算法(快速,网格对齐)
const hasLOS = bresenhamLineOfSight(x1, y1, x2, y2, grid);
// 射线投射(精确,支持浮点坐标)
const hasLOS = raycastLineOfSight(x1, y1, x2, y2, grid, 0.5);
实际示例
游戏角色移动
class MovementSystem {
private grid: GridMap;
private pathfinder: AStarPathfinder;
private smoother: CombinedSmoother;
constructor(width: number, height: number) {
this.grid = createGridMap(width, height);
this.pathfinder = createAStarPathfinder(this.grid);
this.smoother = createCombinedSmoother();
}
findPath(from: IPoint, to: IPoint): IPoint[] | null {
const result = this.pathfinder.findPath(
from.x, from.y,
to.x, to.y
);
if (!result.found) {
return null;
}
// 平滑路径
return this.smoother.smooth(result.path, this.grid);
}
setObstacle(x: number, y: number): void {
this.grid.setWalkable(x, y, false);
}
setTerrain(x: number, y: number, cost: number): void {
this.grid.setCost(x, y, cost);
}
}
动态障碍物
class DynamicPathfinding {
private grid: GridMap;
private pathfinder: AStarPathfinder;
private dynamicObstacles: Set<string> = new Set();
addDynamicObstacle(x: number, y: number): void {
const key = `${x},${y}`;
if (!this.dynamicObstacles.has(key)) {
this.dynamicObstacles.add(key);
this.grid.setWalkable(x, y, false);
}
}
removeDynamicObstacle(x: number, y: number): void {
const key = `${x},${y}`;
if (this.dynamicObstacles.has(key)) {
this.dynamicObstacles.delete(key);
this.grid.setWalkable(x, y, true);
}
}
findPath(from: IPoint, to: IPoint): IPathResult {
return this.pathfinder.findPath(from.x, from.y, to.x, to.y);
}
}
不同地形代价
// 设置不同地形的移动代价
const grid = createGridMap(50, 50);
// 普通地面 - 代价 1(默认)
// 沙地 - 代价 2
for (let y = 10; y < 20; y++) {
for (let x = 0; x < 50; x++) {
grid.setCost(x, y, 2);
}
}
// 沼泽 - 代价 4
for (let y = 30; y < 35; y++) {
for (let x = 20; x < 30; x++) {
grid.setCost(x, y, 4);
}
}
// 寻路时会自动考虑地形代价
const result = pathfinder.findPath(0, 0, 49, 49);
分层寻路
对于大型地图,使用层级化寻路:
class HierarchicalPathfinding {
private coarseGrid: GridMap; // 粗粒度网格
private fineGrid: GridMap; // 细粒度网格
private coarsePathfinder: AStarPathfinder;
private finePathfinder: AStarPathfinder;
private cellSize = 10;
findPath(from: IPoint, to: IPoint): IPoint[] {
// 1. 在粗粒度网格上寻路
const coarseFrom = this.toCoarse(from);
const coarseTo = this.toCoarse(to);
const coarseResult = this.coarsePathfinder.findPath(
coarseFrom.x, coarseFrom.y,
coarseTo.x, coarseTo.y
);
if (!coarseResult.found) {
return [];
}
// 2. 在每个粗粒度单元内进行细粒度寻路
const finePath: IPoint[] = [];
// ... 详细实现略
return finePath;
}
private toCoarse(p: IPoint): IPoint {
return {
x: Math.floor(p.x / this.cellSize),
y: Math.floor(p.y / this.cellSize)
};
}
}
蓝图节点
Pathfinding 模块提供了可视化脚本支持的蓝图节点:
FindPath- 查找路径FindPathSmooth- 查找并平滑路径IsWalkable- 检查位置是否可通行GetPathLength- 获取路径点数GetPathDistance- 获取路径总距离GetPathPoint- 获取路径上的指定点MoveAlongPath- 沿路径移动HasLineOfSight- 检查视线
性能优化
-
限制搜索范围
pathfinder.findPath(x1, y1, x2, y2, { maxNodes: 1000 }); -
使用启发式权重
// 权重 > 1 会更快但可能不是最优路径 pathfinder.findPath(x1, y1, x2, y2, { heuristicWeight: 1.5 }); -
复用寻路器实例
// 创建一次,多次使用 const pathfinder = createAStarPathfinder(grid); -
使用导航网格
- 对于复杂地形,NavMesh 比网格寻路更高效
- 多边形数量远少于网格单元格数量
-
选择合适的启发式
- 4方向移动用
manhattanDistance - 8方向移动用
octileDistance(默认)
- 4方向移动用
网格 vs 导航网格
| 特性 | GridMap | NavMesh |
|---|---|---|
| 适用场景 | 规则瓦片地图 | 复杂多边形地形 |
| 内存占用 | 较高 (width × height) | 较低 (多边形数) |
| 精度 | 网格对齐 | 连续坐标 |
| 动态修改 | 容易 | 需要重建 |
| 设置复杂度 | 简单 | 较复杂 |