docs(modules): 添加框架模块文档 (#350)
* 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 编写。
This commit is contained in:
299
docs/en/modules/pathfinding/index.md
Normal file
299
docs/en/modules/pathfinding/index.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Pathfinding System
|
||||
|
||||
`@esengine/pathfinding` provides a complete 2D pathfinding solution including A* algorithm, grid maps, navigation meshes, and path smoothing.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @esengine/pathfinding
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Grid Map Pathfinding
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
interface IPathResult {
|
||||
readonly found: boolean; // Path found
|
||||
readonly path: readonly IPoint[];// Path points
|
||||
readonly cost: number; // Total cost
|
||||
readonly nodesSearched: number; // Nodes searched
|
||||
}
|
||||
```
|
||||
|
||||
### IPathfindingOptions
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
import { createLineOfSightSmoother } from '@esengine/pathfinding';
|
||||
|
||||
const smoother = createLineOfSightSmoother();
|
||||
const smoothedPath = smoother.smooth(result.path, grid);
|
||||
```
|
||||
|
||||
### Curve Smoothing
|
||||
|
||||
Catmull-Rom spline:
|
||||
|
||||
```typescript
|
||||
import { createCatmullRomSmoother } from '@esengine/pathfinding';
|
||||
|
||||
const smoother = createCatmullRomSmoother(5, 0.5);
|
||||
const curvedPath = smoother.smooth(result.path, grid);
|
||||
```
|
||||
|
||||
### Combined Smoothing
|
||||
|
||||
```typescript
|
||||
import { createCombinedSmoother } from '@esengine/pathfinding';
|
||||
|
||||
const smoother = createCombinedSmoother(5, 0.5);
|
||||
const finalPath = smoother.smooth(result.path, grid);
|
||||
```
|
||||
|
||||
### Line of Sight Functions
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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 path
|
||||
- `FindPathSmooth` - Find and smooth path
|
||||
- `IsWalkable` - Check walkability
|
||||
- `GetPathLength` - Get path point count
|
||||
- `GetPathDistance` - Get total path distance
|
||||
- `GetPathPoint` - Get specific path point
|
||||
- `MoveAlongPath` - Move along path
|
||||
- `HasLineOfSight` - Check line of sight
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Limit search range**: `{ maxNodes: 1000 }`
|
||||
2. **Use heuristic weight**: `{ heuristicWeight: 1.5 }` (faster but may not be optimal)
|
||||
3. **Reuse pathfinder instances**
|
||||
4. **Use NavMesh for complex terrain**
|
||||
5. **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 |
|
||||
Reference in New Issue
Block a user