* 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 编写。
6.7 KiB
6.7 KiB
Pathfinding System
@esengine/pathfinding provides a complete 2D pathfinding solution including A* algorithm, grid maps, navigation meshes, and path smoothing.
Installation
npm install @esengine/pathfinding
Quick Start
Grid Map Pathfinding
import { createGridMap, createAStarPathfinder } from '@esengine/pathfinding';
// Create 20x20 grid
const grid = createGridMap(20, 20);
// Set obstacles
grid.setWalkable(5, 5, false);
grid.setWalkable(5, 6, false);
// Create pathfinder
const pathfinder = createAStarPathfinder(grid);
// Find path
const result = pathfinder.findPath(0, 0, 15, 15);
if (result.found) {
console.log('Path found!');
console.log('Path:', result.path);
console.log('Cost:', result.cost);
}
NavMesh Pathfinding
import { createNavMesh } from '@esengine/pathfinding';
const navmesh = createNavMesh();
// Add polygon areas
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 }
]);
// Auto-build connections
navmesh.build();
// Find path
const result = navmesh.findPath(1, 1, 18, 8);
Core Concepts
IPathResult
interface IPathResult {
readonly found: boolean; // Path found
readonly path: readonly IPoint[];// Path points
readonly cost: number; // Total cost
readonly nodesSearched: number; // Nodes searched
}
IPathfindingOptions
interface IPathfindingOptions {
maxNodes?: number; // Max search nodes (default 10000)
heuristicWeight?: number; // Heuristic weight (>1 faster but may be suboptimal)
allowDiagonal?: boolean; // Allow diagonal movement (default true)
avoidCorners?: boolean; // Avoid corner cutting (default true)
}
Heuristic Functions
| Function | Use Case | Description |
|---|---|---|
manhattanDistance |
4-directional | Manhattan distance |
euclideanDistance |
Any direction | Euclidean distance |
chebyshevDistance |
8-directional | Diagonal cost = 1 |
octileDistance |
8-directional | Diagonal cost = √2 (default) |
Grid Map API
createGridMap
function createGridMap(
width: number,
height: number,
options?: IGridMapOptions
): GridMap
Options:
| Property | Type | Default | Description |
|---|---|---|---|
allowDiagonal |
boolean |
true |
Allow diagonal movement |
diagonalCost |
number |
√2 |
Diagonal movement cost |
avoidCorners |
boolean |
true |
Avoid corner cutting |
heuristic |
HeuristicFunction |
octileDistance |
Heuristic function |
Map Operations
// Check/set walkability
grid.isWalkable(x, y);
grid.setWalkable(x, y, false);
// Set movement cost (e.g., swamp, sand)
grid.setCost(x, y, 2);
// Set rectangle region
grid.setRectWalkable(0, 0, 5, 5, false);
// Load from array (0=walkable, non-0=blocked)
grid.loadFromArray([
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 0]
]);
// Load from string (.=walkable, #=blocked)
grid.loadFromString(`
.....
.#.#.
`);
// Export and reset
console.log(grid.toString());
grid.reset();
A* Pathfinder API
const pathfinder = createAStarPathfinder(grid);
const result = pathfinder.findPath(
startX, startY,
endX, endY,
{ maxNodes: 5000, heuristicWeight: 1.5 }
);
// Pathfinder is reusable
pathfinder.findPath(0, 0, 10, 10);
pathfinder.findPath(5, 5, 15, 15);
NavMesh API
const navmesh = createNavMesh();
// Add convex polygons
const id1 = navmesh.addPolygon(vertices1);
const id2 = navmesh.addPolygon(vertices2);
// Auto-detect shared edges
navmesh.build();
// Or manually set connections
navmesh.setConnection(id1, id2, {
left: { x: 10, y: 0 },
right: { x: 10, y: 10 }
});
// Query and pathfind
const polygon = navmesh.findPolygonAt(5, 5);
navmesh.isWalkable(5, 5);
const result = navmesh.findPath(1, 1, 18, 8);
Path Smoothing
Line of Sight Smoothing
Remove unnecessary waypoints:
import { createLineOfSightSmoother } from '@esengine/pathfinding';
const smoother = createLineOfSightSmoother();
const smoothedPath = smoother.smooth(result.path, grid);
Curve Smoothing
Catmull-Rom spline:
import { createCatmullRomSmoother } from '@esengine/pathfinding';
const smoother = createCatmullRomSmoother(5, 0.5);
const curvedPath = smoother.smooth(result.path, grid);
Combined Smoothing
import { createCombinedSmoother } from '@esengine/pathfinding';
const smoother = createCombinedSmoother(5, 0.5);
const finalPath = smoother.smooth(result.path, grid);
Line of Sight Functions
import { bresenhamLineOfSight, raycastLineOfSight } from '@esengine/pathfinding';
const hasLOS = bresenhamLineOfSight(x1, y1, x2, y2, grid);
const hasLOS2 = raycastLineOfSight(x1, y1, x2, y2, grid, 0.5);
Practical Examples
Dynamic Obstacles
class DynamicPathfinding {
private grid: GridMap;
private pathfinder: AStarPathfinder;
private dynamicObstacles: Set<string> = new Set();
addDynamicObstacle(x: number, y: number): void {
this.dynamicObstacles.add(`${x},${y}`);
this.grid.setWalkable(x, y, false);
}
removeDynamicObstacle(x: number, y: number): void {
this.dynamicObstacles.delete(`${x},${y}`);
this.grid.setWalkable(x, y, true);
}
}
Terrain Costs
const grid = createGridMap(50, 50);
// Normal ground - cost 1 (default)
// Sand - cost 2
for (let y = 10; y < 20; y++) {
for (let x = 0; x < 50; x++) {
grid.setCost(x, y, 2);
}
}
// Swamp - cost 4
for (let y = 30; y < 35; y++) {
for (let x = 20; x < 30; x++) {
grid.setCost(x, y, 4);
}
}
Blueprint Nodes
FindPath- Find pathFindPathSmooth- Find and smooth pathIsWalkable- Check walkabilityGetPathLength- Get path point countGetPathDistance- Get total path distanceGetPathPoint- Get specific path pointMoveAlongPath- Move along pathHasLineOfSight- Check line of sight
Performance Tips
- Limit search range:
{ maxNodes: 1000 } - Use heuristic weight:
{ heuristicWeight: 1.5 }(faster but may not be optimal) - Reuse pathfinder instances
- Use NavMesh for complex terrain
- Choose appropriate heuristic for movement type
Grid vs NavMesh
| Feature | GridMap | NavMesh |
|---|---|---|
| Use Case | Regular tile maps | Complex polygon terrain |
| Memory | Higher (width × height) | Lower (polygon count) |
| Precision | Grid-aligned | Continuous coordinates |
| Dynamic Updates | Easy | Requires rebuild |
| Setup Complexity | Simple | More complex |