@esengine/pathfinding 13.2.0 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{KDTree-2rs2EXvm.d.ts → CollisionResolver-CSgWsegP.d.ts} +122 -86
- package/dist/FlowController-Dc3nuLq5.d.ts +2751 -0
- package/dist/KDTree-BRpn7O8K.d.ts +216 -0
- package/dist/avoidance.d.ts +26 -4
- package/dist/avoidance.js +10 -2
- package/dist/{chunk-JTZP55BJ.js → chunk-3VEX32JO.js} +385 -9
- package/dist/chunk-3VEX32JO.js.map +1 -0
- package/dist/chunk-H5EFZBBT.js +1 -0
- package/dist/chunk-NIKT3PQC.js +3811 -0
- package/dist/chunk-NIKT3PQC.js.map +1 -0
- package/dist/ecs.d.ts +440 -647
- package/dist/ecs.js +1003 -1399
- package/dist/ecs.js.map +1 -1
- package/dist/index.d.ts +153 -711
- package/dist/index.js +1335 -1735
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/IIncrementalPathfinding-3qs7e_pO.d.ts +0 -450
- package/dist/LinearProgram-DyD3pI6v.d.ts +0 -56
- package/dist/chunk-JTZP55BJ.js.map +0 -1
- package/dist/chunk-KEYTX37K.js +0 -1
- package/dist/chunk-VNC2YAAL.js +0 -1650
- package/dist/chunk-VNC2YAAL.js.map +0 -1
- /package/dist/{chunk-KEYTX37K.js.map → chunk-H5EFZBBT.js.map} +0 -0
package/dist/ecs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ecs/PathfindingAgentComponent.ts","../src/ecs/PathfindingMapComponent.ts","../src/ecs/PathfindingSystem.ts","../src/ecs/AvoidanceAgentComponent.ts","../src/ecs/AvoidanceWorldComponent.ts","../src/ecs/LocalAvoidanceSystem.ts"],"sourcesContent":["/**\n * @zh 寻路代理组件\n * @en Pathfinding Agent Component\n */\n\nimport {\n Component,\n ECSComponent,\n Serializable,\n Serialize,\n Property\n} from '@esengine/ecs-framework';\nimport type { IPoint } from '../core/IPathfinding';\nimport { PathfindingState } from '../core/IIncrementalPathfinding';\n\n// =============================================================================\n// 寻路代理组件 | Pathfinding Agent Component\n// =============================================================================\n\n/**\n * @zh 寻路代理组件\n * @en Pathfinding Agent Component\n *\n * @zh 附加到需要寻路的实体上,管理寻路请求和结果\n * @en Attach to entities that need pathfinding, manages path requests and results\n *\n * @example\n * ```typescript\n * const entity = scene.createEntity('Agent');\n * const agent = entity.addComponent(new PathfindingAgentComponent());\n *\n * // Set initial position\n * agent.x = 10;\n * agent.y = 10;\n *\n * // Request path to target\n * agent.requestPathTo(50, 50);\n *\n * // In movement system, follow the path\n * const waypoint = agent.getNextWaypoint();\n * if (waypoint) {\n * // Move towards waypoint\n * // When reached, call agent.advanceWaypoint()\n * }\n * ```\n */\n@ECSComponent('PathfindingAgent')\n@Serializable({ version: 1, typeId: 'PathfindingAgent' })\nexport class PathfindingAgentComponent extends Component {\n // =========================================================================\n // 位置属性 | Position Properties\n // =========================================================================\n\n /**\n * @zh 当前位置 X 坐标\n * @en Current position X coordinate\n */\n @Serialize()\n @Property({ type: 'number', label: 'Position X' })\n x: number = 0;\n\n /**\n * @zh 当前位置 Y 坐标\n * @en Current position Y coordinate\n */\n @Serialize()\n @Property({ type: 'number', label: 'Position Y' })\n y: number = 0;\n\n // =========================================================================\n // 目标属性 | Target Properties\n // =========================================================================\n\n /**\n * @zh 目标位置 X 坐标\n * @en Target position X coordinate\n */\n @Serialize()\n @Property({ type: 'number', label: 'Target X' })\n targetX: number = 0;\n\n /**\n * @zh 目标位置 Y 坐标\n * @en Target position Y coordinate\n */\n @Serialize()\n @Property({ type: 'number', label: 'Target Y' })\n targetY: number = 0;\n\n /**\n * @zh 是否有新的寻路请求待处理\n * @en Whether there is a new path request pending\n */\n hasRequest: boolean = false;\n\n // =========================================================================\n // 配置属性 | Configuration Properties\n // =========================================================================\n\n /**\n * @zh 寻路优先级(数值越小优先级越高)\n * @en Pathfinding priority (lower number = higher priority)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Priority', min: 0, max: 100 })\n priority: number = 50;\n\n /**\n * @zh 每帧最大迭代次数\n * @en Maximum iterations per frame\n */\n @Serialize()\n @Property({ type: 'number', label: 'Max Iterations/Frame', min: 10, max: 1000 })\n maxIterationsPerFrame: number = 100;\n\n /**\n * @zh 是否启用动态重规划\n * @en Whether dynamic replanning is enabled\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Dynamic Replan' })\n enableDynamicReplan: boolean = true;\n\n /**\n * @zh 向前探测距离(用于障碍物检测)\n * @en Lookahead distance for obstacle detection\n */\n @Serialize()\n @Property({ type: 'number', label: 'Lookahead Distance', min: 1, max: 20 })\n lookaheadDistance: number = 5;\n\n /**\n * @zh 路径验证间隔(帧数)\n * @en Path validation interval (in frames)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Validation Interval', min: 1, max: 60 })\n validationInterval: number = 10;\n\n // =========================================================================\n // 运行时状态(不序列化)| Runtime State (not serialized)\n // =========================================================================\n\n /**\n * @zh 当前寻路状态\n * @en Current pathfinding state\n */\n state: PathfindingState = PathfindingState.Idle;\n\n /**\n * @zh 当前请求 ID\n * @en Current request ID\n */\n currentRequestId: number = -1;\n\n /**\n * @zh 当前路径点列表\n * @en Current path waypoints\n */\n path: IPoint[] = [];\n\n /**\n * @zh 当前路径索引\n * @en Current path index\n */\n pathIndex: number = 0;\n\n /**\n * @zh 路径总代价\n * @en Total path cost\n */\n pathCost: number = 0;\n\n /**\n * @zh 寻路进度 (0-1)\n * @en Pathfinding progress (0-1)\n */\n progress: number = 0;\n\n /**\n * @zh 上次验证的帧号\n * @en Last validation frame number\n */\n lastValidationFrame: number = 0;\n\n /**\n * @zh 寻路完成回调\n * @en Pathfinding complete callback\n */\n onPathComplete?: (found: boolean, path: readonly IPoint[]) => void;\n\n /**\n * @zh 寻路进度回调\n * @en Pathfinding progress callback\n */\n onPathProgress?: (progress: number) => void;\n\n // =========================================================================\n // 公共方法 | Public Methods\n // =========================================================================\n\n /**\n * @zh 请求寻路到目标位置\n * @en Request path to target position\n *\n * @param targetX - @zh 目标 X 坐标 @en Target X coordinate\n * @param targetY - @zh 目标 Y 坐标 @en Target Y coordinate\n */\n requestPathTo(targetX: number, targetY: number): void {\n this.targetX = targetX;\n this.targetY = targetY;\n this.hasRequest = true;\n this.state = PathfindingState.Idle;\n this.progress = 0;\n }\n\n /**\n * @zh 取消当前寻路\n * @en Cancel current pathfinding\n */\n cancelPath(): void {\n this.hasRequest = false;\n this.state = PathfindingState.Cancelled;\n this.path = [];\n this.pathIndex = 0;\n this.progress = 0;\n this.currentRequestId = -1;\n }\n\n /**\n * @zh 获取下一个路径点\n * @en Get next waypoint\n *\n * @returns @zh 下一个路径点或 null @en Next waypoint or null\n */\n getNextWaypoint(): IPoint | null {\n if (this.pathIndex < this.path.length) {\n return this.path[this.pathIndex];\n }\n return null;\n }\n\n /**\n * @zh 前进到下一个路径点\n * @en Advance to next waypoint\n */\n advanceWaypoint(): void {\n if (this.pathIndex < this.path.length) {\n this.pathIndex++;\n }\n }\n\n /**\n * @zh 检查是否到达路径终点\n * @en Check if reached path end\n *\n * @returns @zh 是否到达终点 @en Whether reached end\n */\n isPathComplete(): boolean {\n return this.pathIndex >= this.path.length;\n }\n\n /**\n * @zh 检查是否正在寻路\n * @en Check if pathfinding is in progress\n *\n * @returns @zh 是否正在寻路 @en Whether pathfinding is in progress\n */\n isSearching(): boolean {\n return this.state === PathfindingState.InProgress;\n }\n\n /**\n * @zh 检查是否有有效路径\n * @en Check if has valid path\n *\n * @returns @zh 是否有有效路径 @en Whether has valid path\n */\n hasValidPath(): boolean {\n return this.state === PathfindingState.Completed && this.path.length > 0;\n }\n\n /**\n * @zh 获取剩余路径点数量\n * @en Get remaining waypoint count\n *\n * @returns @zh 剩余路径点数量 @en Remaining waypoint count\n */\n getRemainingWaypointCount(): number {\n return Math.max(0, this.path.length - this.pathIndex);\n }\n\n /**\n * @zh 获取当前路径的总长度\n * @en Get total path length\n *\n * @returns @zh 路径总长度 @en Total path length\n */\n getPathLength(): number {\n return this.path.length;\n }\n\n /**\n * @zh 重置组件状态\n * @en Reset component state\n */\n reset(): void {\n this.state = PathfindingState.Idle;\n this.currentRequestId = -1;\n this.path = [];\n this.pathIndex = 0;\n this.pathCost = 0;\n this.progress = 0;\n this.hasRequest = false;\n this.lastValidationFrame = 0;\n }\n\n /**\n * @zh 组件从实体移除时调用\n * @en Called when component is removed from entity\n */\n public onRemovedFromEntity(): void {\n this.reset();\n this.onPathComplete = undefined;\n this.onPathProgress = undefined;\n }\n}\n","/**\n * @zh 寻路地图组件\n * @en Pathfinding Map Component\n */\n\nimport {\n Component,\n ECSComponent,\n Serializable,\n Serialize,\n Property\n} from '@esengine/ecs-framework';\nimport type { IPathfindingMap, IPathSmoother } from '../core/IPathfinding';\nimport type { IIncrementalPathfinder } from '../core/IIncrementalPathfinding';\n\n// =============================================================================\n// 地图类型 | Map Type\n// =============================================================================\n\n/**\n * @zh 地图类型\n * @en Map type\n */\nexport type PathfindingMapType = 'grid' | 'navmesh';\n\n// =============================================================================\n// 寻路地图组件 | Pathfinding Map Component\n// =============================================================================\n\n/**\n * @zh 寻路地图组件\n * @en Pathfinding Map Component\n *\n * @zh 挂载在场景实体上,持有地图实例和增量寻路器\n * @en Attached to scene entity, holds map instance and incremental pathfinder\n *\n * @example\n * ```typescript\n * const mapEntity = scene.createEntity('PathfindingMap');\n * const mapComp = mapEntity.addComponent(new PathfindingMapComponent());\n *\n * // Configure map\n * mapComp.width = 100;\n * mapComp.height = 100;\n * mapComp.iterationsBudget = 2000;\n *\n * // Map and pathfinder will be initialized by PathfindingSystem\n * ```\n */\n@ECSComponent('PathfindingMap')\n@Serializable({ version: 1, typeId: 'PathfindingMap' })\nexport class PathfindingMapComponent extends Component {\n // =========================================================================\n // 地图配置 | Map Configuration\n // =========================================================================\n\n /**\n * @zh 地图类型\n * @en Map type\n */\n @Serialize()\n @Property({\n type: 'enum',\n label: 'Map Type',\n options: [\n { value: 'grid', label: 'Grid' },\n { value: 'navmesh', label: 'NavMesh' }\n ]\n })\n mapType: PathfindingMapType = 'grid';\n\n /**\n * @zh 网格宽度(仅 grid 类型)\n * @en Grid width (grid type only)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Map Width', min: 1, max: 10000 })\n width: number = 100;\n\n /**\n * @zh 网格高度(仅 grid 类型)\n * @en Grid height (grid type only)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Map Height', min: 1, max: 10000 })\n height: number = 100;\n\n /**\n * @zh 是否允许对角移动\n * @en Whether diagonal movement is allowed\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Allow Diagonal' })\n allowDiagonal: boolean = true;\n\n /**\n * @zh 是否避免穿角\n * @en Whether to avoid corner cutting\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Avoid Corners' })\n avoidCorners: boolean = true;\n\n // =========================================================================\n // 系统配置 | System Configuration\n // =========================================================================\n\n /**\n * @zh 每帧处理的最大代理数\n * @en Maximum agents processed per frame\n */\n @Serialize()\n @Property({ type: 'number', label: 'Max Agents/Frame', min: 1, max: 100 })\n maxAgentsPerFrame: number = 10;\n\n /**\n * @zh 每帧总迭代次数预算\n * @en Total iterations budget per frame\n */\n @Serialize()\n @Property({ type: 'number', label: 'Iterations Budget', min: 100, max: 10000 })\n iterationsBudget: number = 1000;\n\n /**\n * @zh 是否启用路径平滑\n * @en Whether path smoothing is enabled\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Enable Smoothing' })\n enableSmoothing: boolean = true;\n\n /**\n * @zh 路径平滑类型\n * @en Path smoothing type\n */\n @Serialize()\n @Property({\n type: 'enum',\n label: 'Smoothing Type',\n options: [\n { value: 'los', label: 'Line of Sight' },\n { value: 'catmullrom', label: 'Catmull-Rom' },\n { value: 'combined', label: 'Combined' }\n ]\n })\n smoothingType: 'los' | 'catmullrom' | 'combined' = 'los';\n\n // =========================================================================\n // 缓存配置 | Cache Configuration\n // =========================================================================\n\n /**\n * @zh 是否启用路径缓存\n * @en Whether path caching is enabled\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Enable Cache' })\n enableCache: boolean = true;\n\n /**\n * @zh 缓存最大条目数\n * @en Maximum cache entries\n */\n @Serialize()\n @Property({ type: 'number', label: 'Cache Size', min: 100, max: 10000 })\n cacheMaxEntries: number = 1000;\n\n /**\n * @zh 缓存过期时间(毫秒),0 表示不过期\n * @en Cache TTL in milliseconds, 0 means no expiration\n */\n @Serialize()\n @Property({ type: 'number', label: 'Cache TTL (ms)', min: 0, max: 60000 })\n cacheTtlMs: number = 5000;\n\n // =========================================================================\n // 调试配置 | Debug Configuration\n // =========================================================================\n\n /**\n * @zh 是否显示调试信息\n * @en Whether to show debug info\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Debug Mode' })\n debugMode: boolean = false;\n\n /**\n * @zh 是否显示网格\n * @en Whether to show grid\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Show Grid' })\n showGrid: boolean = false;\n\n /**\n * @zh 是否显示路径\n * @en Whether to show paths\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Show Paths' })\n showPaths: boolean = false;\n\n // =========================================================================\n // 运行时实例(不序列化)| Runtime Instances (not serialized)\n // =========================================================================\n\n /**\n * @zh 地图实例\n * @en Map instance\n */\n map: IPathfindingMap | null = null;\n\n /**\n * @zh 增量寻路器实例\n * @en Incremental pathfinder instance\n */\n pathfinder: IIncrementalPathfinder | null = null;\n\n /**\n * @zh 路径平滑器实例\n * @en Path smoother instance\n */\n smoother: IPathSmoother | null = null;\n\n /**\n * @zh 是否已初始化\n * @en Whether initialized\n */\n initialized: boolean = false;\n\n // =========================================================================\n // 统计信息 | Statistics\n // =========================================================================\n\n /**\n * @zh 当前活跃请求数\n * @en Current active request count\n */\n activeRequests: number = 0;\n\n /**\n * @zh 本帧使用的迭代次数\n * @en Iterations used this frame\n */\n iterationsUsedThisFrame: number = 0;\n\n /**\n * @zh 本帧处理的代理数\n * @en Agents processed this frame\n */\n agentsProcessedThisFrame: number = 0;\n\n /**\n * @zh 缓存命中次数\n * @en Cache hit count\n */\n cacheHits: number = 0;\n\n /**\n * @zh 缓存未命中次数\n * @en Cache miss count\n */\n cacheMisses: number = 0;\n\n // =========================================================================\n // 公共方法 | Public Methods\n // =========================================================================\n\n /**\n * @zh 设置网格单元格是否可通行\n * @en Set grid cell walkability\n *\n * @param x - @zh X 坐标 @en X coordinate\n * @param y - @zh Y 坐标 @en Y coordinate\n * @param walkable - @zh 是否可通行 @en Is walkable\n */\n setWalkable(x: number, y: number, walkable: boolean): void {\n if (this.map && 'setWalkable' in this.map) {\n (this.map as { setWalkable(x: number, y: number, walkable: boolean): void })\n .setWalkable(x, y, walkable);\n\n if (this.pathfinder) {\n this.pathfinder.notifyObstacleChange(x - 1, y - 1, x + 1, y + 1);\n }\n }\n }\n\n /**\n * @zh 设置矩形区域是否可通行\n * @en Set rectangular area walkability\n *\n * @param x - @zh 起始 X @en Start X\n * @param y - @zh 起始 Y @en Start Y\n * @param width - @zh 宽度 @en Width\n * @param height - @zh 高度 @en Height\n * @param walkable - @zh 是否可通行 @en Is walkable\n */\n setRectWalkable(\n x: number,\n y: number,\n rectWidth: number,\n rectHeight: number,\n walkable: boolean\n ): void {\n if (this.map && 'setRectWalkable' in this.map) {\n (this.map as { setRectWalkable(x: number, y: number, w: number, h: number, walkable: boolean): void })\n .setRectWalkable(x, y, rectWidth, rectHeight, walkable);\n\n if (this.pathfinder) {\n this.pathfinder.notifyObstacleChange(\n x - 1,\n y - 1,\n x + rectWidth + 1,\n y + rectHeight + 1\n );\n }\n }\n }\n\n /**\n * @zh 检查位置是否可通行\n * @en Check if position is walkable\n *\n * @param x - @zh X 坐标 @en X coordinate\n * @param y - @zh Y 坐标 @en Y coordinate\n * @returns @zh 是否可通行 @en Is walkable\n */\n isWalkable(x: number, y: number): boolean {\n return this.map?.isWalkable(x, y) ?? false;\n }\n\n /**\n * @zh 重置统计信息\n * @en Reset statistics\n */\n resetStats(): void {\n this.iterationsUsedThisFrame = 0;\n this.agentsProcessedThisFrame = 0;\n }\n\n /**\n * @zh 获取剩余迭代预算\n * @en Get remaining iteration budget\n *\n * @returns @zh 剩余预算 @en Remaining budget\n */\n getRemainingBudget(): number {\n return Math.max(0, this.iterationsBudget - this.iterationsUsedThisFrame);\n }\n\n /**\n * @zh 获取缓存统计信息\n * @en Get cache statistics\n *\n * @returns @zh 缓存统计 @en Cache statistics\n */\n getCacheStats(): { enabled: boolean; hits: number; misses: number; hitRate: number } {\n const pathfinderWithCache = this.pathfinder as { getCacheStats?: () => { hits: number; misses: number; hitRate: number } };\n if (pathfinderWithCache?.getCacheStats) {\n const stats = pathfinderWithCache.getCacheStats();\n this.cacheHits = stats.hits;\n this.cacheMisses = stats.misses;\n return {\n enabled: this.enableCache,\n hits: stats.hits,\n misses: stats.misses,\n hitRate: stats.hitRate\n };\n }\n return {\n enabled: this.enableCache,\n hits: this.cacheHits,\n misses: this.cacheMisses,\n hitRate: this.cacheHits + this.cacheMisses > 0\n ? this.cacheHits / (this.cacheHits + this.cacheMisses)\n : 0\n };\n }\n\n /**\n * @zh 组件从实体移除时调用\n * @en Called when component is removed from entity\n */\n public onRemovedFromEntity(): void {\n if (this.pathfinder) {\n this.pathfinder.clear();\n }\n\n this.map = null;\n this.pathfinder = null;\n this.smoother = null;\n this.initialized = false;\n }\n}\n","/**\n * @zh 寻路系统\n * @en Pathfinding System\n */\n\nimport {\n EntitySystem,\n Matcher,\n ECSSystem,\n type Entity\n} from '@esengine/ecs-framework';\nimport { PathfindingAgentComponent } from './PathfindingAgentComponent';\nimport { PathfindingMapComponent } from './PathfindingMapComponent';\nimport { PathfindingState } from '../core/IIncrementalPathfinding';\nimport type { IIncrementalPathfinder, IPathProgress } from '../core/IIncrementalPathfinding';\nimport { IncrementalAStarPathfinder } from '../core/IncrementalAStarPathfinder';\nimport { GridMap } from '../grid/GridMap';\nimport { LineOfSightSmoother, CatmullRomSmoother, CombinedSmoother } from '../smoothing/PathSmoother';\nimport { PathValidator } from '../core/PathValidator';\n\n// =============================================================================\n// 代理队列项 | Agent Queue Item\n// =============================================================================\n\n/**\n * @zh 代理队列项\n * @en Agent queue item\n */\ninterface AgentQueueItem {\n entity: Entity;\n component: PathfindingAgentComponent;\n}\n\n// =============================================================================\n// 寻路系统 | Pathfinding System\n// =============================================================================\n\n/**\n * @zh 寻路系统\n * @en Pathfinding System\n *\n * @zh 处理所有 PathfindingAgentComponent,支持时间切片和动态重规划\n * @en Processes all PathfindingAgentComponents, supports time slicing and dynamic replanning\n *\n * @example\n * ```typescript\n * // Add system to scene\n * scene.addSystem(new PathfindingSystem());\n *\n * // Create map entity\n * const mapEntity = scene.createEntity('Map');\n * mapEntity.addComponent(new PathfindingMapComponent());\n *\n * // Create agents\n * const agent = scene.createEntity('Agent');\n * const pathAgent = agent.addComponent(new PathfindingAgentComponent());\n * pathAgent.requestPathTo(50, 50);\n *\n * // System handles pathfinding automatically each frame\n * ```\n */\n@ECSSystem('Pathfinding', { updateOrder: 0 })\nexport class PathfindingSystem extends EntitySystem {\n private mapEntity: Entity | null = null;\n private mapComponent: PathfindingMapComponent | null = null;\n private pathValidator: PathValidator;\n\n private agentQueue: AgentQueueItem[] = [];\n private frameCounter: number = 0;\n\n constructor() {\n super(Matcher.all(PathfindingAgentComponent));\n this.pathValidator = new PathValidator();\n }\n\n /**\n * @zh 系统初始化\n * @en System initialization\n */\n protected onInitialize(): void {\n this.findMapEntity();\n this.initializeMap();\n }\n\n /**\n * @zh 系统激活时调用\n * @en Called when system is enabled\n */\n protected onEnable(): void {\n this.findMapEntity();\n }\n\n /**\n * @zh 处理实体\n * @en Process entities\n */\n protected process(entities: readonly Entity[]): void {\n if (!this.mapComponent?.pathfinder) {\n this.findMapEntity();\n if (!this.mapComponent?.pathfinder) {\n return;\n }\n }\n\n this.frameCounter++;\n this.mapComponent.resetStats();\n\n this.buildAgentQueue(entities);\n\n this.processAgentsWithBudget();\n\n this.validatePaths(entities);\n }\n\n // =========================================================================\n // 私有方法 | Private Methods\n // =========================================================================\n\n /**\n * @zh 查找地图实体\n * @en Find map entity\n */\n private findMapEntity(): void {\n if (!this.scene) return;\n\n const entities = this.scene.entities.findEntitiesWithComponent(PathfindingMapComponent);\n if (entities.length > 0) {\n const entity = entities[0]!;\n const mapComp = entity.getComponent(PathfindingMapComponent);\n if (mapComp) {\n this.mapEntity = entity;\n this.mapComponent = mapComp;\n\n if (!mapComp.initialized) {\n this.initializeMap();\n }\n }\n }\n }\n\n /**\n * @zh 初始化地图\n * @en Initialize map\n */\n private initializeMap(): void {\n if (!this.mapComponent) return;\n if (this.mapComponent.initialized) return;\n\n if (!this.mapComponent.map) {\n if (this.mapComponent.mapType === 'grid') {\n this.mapComponent.map = new GridMap(\n this.mapComponent.width,\n this.mapComponent.height,\n {\n allowDiagonal: this.mapComponent.allowDiagonal,\n avoidCorners: this.mapComponent.avoidCorners\n }\n );\n }\n }\n\n if (!this.mapComponent.pathfinder && this.mapComponent.map) {\n this.mapComponent.pathfinder = new IncrementalAStarPathfinder(\n this.mapComponent.map,\n {\n enableCache: this.mapComponent.enableCache,\n cacheConfig: {\n maxEntries: this.mapComponent.cacheMaxEntries,\n ttlMs: this.mapComponent.cacheTtlMs\n }\n }\n );\n }\n\n if (!this.mapComponent.smoother && this.mapComponent.enableSmoothing) {\n switch (this.mapComponent.smoothingType) {\n case 'catmullrom':\n this.mapComponent.smoother = new CatmullRomSmoother();\n break;\n case 'combined':\n this.mapComponent.smoother = new CombinedSmoother();\n break;\n case 'los':\n default:\n this.mapComponent.smoother = new LineOfSightSmoother();\n break;\n }\n }\n\n this.mapComponent.initialized = true;\n }\n\n /**\n * @zh 构建代理优先级队列\n * @en Build agent priority queue\n */\n private buildAgentQueue(entities: readonly Entity[]): void {\n this.agentQueue.length = 0;\n\n for (const entity of entities) {\n const agent = entity.getComponent(PathfindingAgentComponent);\n if (!agent) continue;\n\n if (!agent.hasRequest &&\n (agent.state === PathfindingState.Idle ||\n agent.state === PathfindingState.Completed ||\n agent.state === PathfindingState.Cancelled)) {\n continue;\n }\n\n this.agentQueue.push({ entity, component: agent });\n }\n\n this.agentQueue.sort((a, b) => a.component.priority - b.component.priority);\n }\n\n /**\n * @zh 使用预算处理代理\n * @en Process agents with budget\n */\n private processAgentsWithBudget(): void {\n const pathfinder = this.mapComponent!.pathfinder!;\n const maxAgents = this.mapComponent!.maxAgentsPerFrame;\n let remainingBudget = this.mapComponent!.iterationsBudget;\n let agentsProcessed = 0;\n\n for (const { component: agent } of this.agentQueue) {\n if (agentsProcessed >= maxAgents || remainingBudget <= 0) {\n break;\n }\n\n if (agent.hasRequest && agent.state === PathfindingState.Idle) {\n this.startNewRequest(agent, pathfinder);\n }\n\n if (agent.state === PathfindingState.InProgress) {\n const iterations = Math.min(\n agent.maxIterationsPerFrame,\n remainingBudget\n );\n\n const progress = pathfinder.step(agent.currentRequestId, iterations);\n this.updateAgentFromProgress(agent, progress, pathfinder);\n\n remainingBudget -= progress.nodesSearched;\n this.mapComponent!.iterationsUsedThisFrame += progress.nodesSearched;\n }\n\n agentsProcessed++;\n }\n\n this.mapComponent!.agentsProcessedThisFrame = agentsProcessed;\n }\n\n /**\n * @zh 启动新的寻路请求\n * @en Start new pathfinding request\n */\n private startNewRequest(\n agent: PathfindingAgentComponent,\n pathfinder: IIncrementalPathfinder\n ): void {\n if (agent.currentRequestId >= 0) {\n pathfinder.cancel(agent.currentRequestId);\n pathfinder.cleanup(agent.currentRequestId);\n }\n\n const request = pathfinder.requestPath(\n agent.x,\n agent.y,\n agent.targetX,\n agent.targetY,\n { priority: agent.priority }\n );\n\n agent.currentRequestId = request.id;\n agent.state = PathfindingState.InProgress;\n agent.hasRequest = false;\n agent.progress = 0;\n agent.path = [];\n agent.pathIndex = 0;\n\n this.mapComponent!.activeRequests++;\n }\n\n /**\n * @zh 从进度更新代理状态\n * @en Update agent state from progress\n */\n private updateAgentFromProgress(\n agent: PathfindingAgentComponent,\n progress: IPathProgress,\n pathfinder: IIncrementalPathfinder\n ): void {\n agent.state = progress.state;\n agent.progress = progress.estimatedProgress;\n\n agent.onPathProgress?.(progress.estimatedProgress);\n\n if (progress.state === PathfindingState.Completed) {\n const result = pathfinder.getResult(agent.currentRequestId);\n if (result && result.found) {\n const smoother = this.mapComponent?.smoother;\n const map = this.mapComponent?.map;\n\n if (smoother && map && this.mapComponent?.enableSmoothing) {\n agent.path = smoother.smooth(result.path, map);\n } else {\n agent.path = [...result.path];\n }\n\n agent.pathIndex = 0;\n agent.pathCost = result.cost;\n\n agent.onPathComplete?.(true, agent.path);\n } else {\n agent.path = [];\n agent.state = PathfindingState.Failed;\n agent.onPathComplete?.(false, []);\n }\n\n pathfinder.cleanup(agent.currentRequestId);\n this.mapComponent!.activeRequests--;\n } else if (progress.state === PathfindingState.Failed) {\n agent.path = [];\n agent.onPathComplete?.(false, []);\n pathfinder.cleanup(agent.currentRequestId);\n this.mapComponent!.activeRequests--;\n }\n }\n\n /**\n * @zh 周期性验证路径有效性\n * @en Periodically validate path validity\n */\n private validatePaths(entities: readonly Entity[]): void {\n const map = this.mapComponent?.map;\n if (!map) return;\n\n for (const entity of entities) {\n const agent = entity.getComponent(PathfindingAgentComponent);\n if (!agent || !agent.enableDynamicReplan) continue;\n if (agent.path.length === 0 || agent.isPathComplete()) continue;\n\n if (this.frameCounter - agent.lastValidationFrame < agent.validationInterval) {\n continue;\n }\n\n agent.lastValidationFrame = this.frameCounter;\n\n const checkEnd = Math.min(\n agent.pathIndex + agent.lookaheadDistance,\n agent.path.length\n );\n\n const result = this.pathValidator.validatePath(\n agent.path,\n agent.pathIndex,\n checkEnd,\n map\n );\n\n if (!result.valid) {\n agent.x = agent.path[agent.pathIndex]?.x ?? agent.x;\n agent.y = agent.path[agent.pathIndex]?.y ?? agent.y;\n agent.requestPathTo(agent.targetX, agent.targetY);\n }\n }\n }\n}\n","/**\n * @zh 避让代理组件\n * @en Avoidance Agent Component\n */\n\nimport {\n Component,\n ECSComponent,\n Serializable,\n Serialize,\n Property\n} from '@esengine/ecs-framework';\nimport { DEFAULT_AGENT_PARAMS } from '../avoidance/ILocalAvoidance';\n\n// =============================================================================\n// 避让代理组件 | Avoidance Agent Component\n// =============================================================================\n\n/**\n * @zh 避让代理组件\n * @en Avoidance Agent Component\n *\n * @zh 附加到需要局部避让的实体上,与 ORCA 系统配合使用\n * @en Attach to entities that need local avoidance, works with ORCA system\n *\n * @example\n * ```typescript\n * const entity = scene.createEntity('Monster');\n *\n * // 添加避让代理\n * const avoidance = entity.addComponent(new AvoidanceAgentComponent());\n * avoidance.radius = 0.5;\n * avoidance.maxSpeed = 5.0;\n *\n * // 设置首选速度(朝向目标)\n * avoidance.setPreferredVelocityTowards(targetX, targetY, currentX, currentY);\n *\n * // 系统计算后,使用新速度更新位置\n * // After system computes, use new velocity to update position\n * x += avoidance.newVelocityX * deltaTime;\n * y += avoidance.newVelocityY * deltaTime;\n * ```\n */\n@ECSComponent('AvoidanceAgent')\n@Serializable({ version: 1, typeId: 'AvoidanceAgent' })\nexport class AvoidanceAgentComponent extends Component {\n // =========================================================================\n // 物理属性 | Physical Properties\n // =========================================================================\n\n /**\n * @zh 代理半径\n * @en Agent radius\n *\n * @zh 用于碰撞检测和避让计算\n * @en Used for collision detection and avoidance computation\n */\n @Serialize()\n @Property({ type: 'number', label: 'Radius', min: 0.1, max: 10 })\n radius: number = DEFAULT_AGENT_PARAMS.radius;\n\n /**\n * @zh 最大速度\n * @en Maximum speed\n */\n @Serialize()\n @Property({ type: 'number', label: 'Max Speed', min: 0.1, max: 100 })\n maxSpeed: number = DEFAULT_AGENT_PARAMS.maxSpeed;\n\n // =========================================================================\n // ORCA 参数 | ORCA Parameters\n // =========================================================================\n\n /**\n * @zh 邻居检测距离\n * @en Neighbor detection distance\n *\n * @zh 只考虑此范围内的其他代理\n * @en Only considers other agents within this range\n */\n @Serialize()\n @Property({ type: 'number', label: 'Neighbor Dist', min: 1, max: 100 })\n neighborDist: number = DEFAULT_AGENT_PARAMS.neighborDist;\n\n /**\n * @zh 最大邻居数量\n * @en Maximum number of neighbors to consider\n *\n * @zh 限制计算量,优先考虑最近的邻居\n * @en Limits computation, prioritizes closest neighbors\n */\n @Serialize()\n @Property({ type: 'number', label: 'Max Neighbors', min: 1, max: 50 })\n maxNeighbors: number = DEFAULT_AGENT_PARAMS.maxNeighbors;\n\n /**\n * @zh 代理避让时间视野(秒)\n * @en Time horizon for agent avoidance (seconds)\n *\n * @zh 更大的值会让代理更早开始避让,但可能导致过于保守\n * @en Larger values make agents start avoiding earlier, but may be too conservative\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Horizon', min: 0.1, max: 10 })\n timeHorizon: number = DEFAULT_AGENT_PARAMS.timeHorizon;\n\n /**\n * @zh 障碍物避让时间视野(秒)\n * @en Time horizon for obstacle avoidance (seconds)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Horizon Obst', min: 0.1, max: 10 })\n timeHorizonObst: number = DEFAULT_AGENT_PARAMS.timeHorizonObst;\n\n // =========================================================================\n // 位置和速度(运行时状态)| Position & Velocity (Runtime State)\n // =========================================================================\n\n /**\n * @zh 当前位置 X\n * @en Current position X\n *\n * @zh 如果实体有 Transform 组件,系统会自动同步\n * @en If entity has Transform component, system will sync automatically\n */\n positionX: number = 0;\n\n /**\n * @zh 当前位置 Y\n * @en Current position Y\n */\n positionY: number = 0;\n\n /**\n * @zh 当前速度 X\n * @en Current velocity X\n */\n velocityX: number = 0;\n\n /**\n * @zh 当前速度 Y\n * @en Current velocity Y\n */\n velocityY: number = 0;\n\n /**\n * @zh 首选速度 X(通常指向目标方向)\n * @en Preferred velocity X (usually towards target)\n */\n preferredVelocityX: number = 0;\n\n /**\n * @zh 首选速度 Y\n * @en Preferred velocity Y\n */\n preferredVelocityY: number = 0;\n\n /**\n * @zh ORCA 计算的新速度 X\n * @en New velocity X computed by ORCA\n */\n newVelocityX: number = 0;\n\n /**\n * @zh ORCA 计算的新速度 Y\n * @en New velocity Y computed by ORCA\n */\n newVelocityY: number = 0;\n\n // =========================================================================\n // 配置选项 | Configuration Options\n // =========================================================================\n\n /**\n * @zh 是否启用避让\n * @en Whether avoidance is enabled\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Enabled' })\n enabled: boolean = true;\n\n /**\n * @zh 是否自动应用新速度\n * @en Whether to automatically apply new velocity\n *\n * @zh 如果为 true,系统会在计算后自动将 newVelocity 赋值给 velocity\n * @en If true, system will automatically assign newVelocity to velocity after computation\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Auto Apply' })\n autoApplyVelocity: boolean = true;\n\n // =========================================================================\n // 公共方法 | Public Methods\n // =========================================================================\n\n /**\n * @zh 设置位置\n * @en Set position\n */\n setPosition(x: number, y: number): void {\n this.positionX = x;\n this.positionY = y;\n }\n\n /**\n * @zh 设置当前速度\n * @en Set current velocity\n */\n setVelocity(x: number, y: number): void {\n this.velocityX = x;\n this.velocityY = y;\n }\n\n /**\n * @zh 设置首选速度\n * @en Set preferred velocity\n */\n setPreferredVelocity(x: number, y: number): void {\n this.preferredVelocityX = x;\n this.preferredVelocityY = y;\n }\n\n /**\n * @zh 设置首选速度朝向目标\n * @en Set preferred velocity towards target\n *\n * @param targetX - @zh 目标 X @en Target X\n * @param targetY - @zh 目标 Y @en Target Y\n * @param currentX - @zh 当前 X(可选,默认使用 positionX)@en Current X (optional, defaults to positionX)\n * @param currentY - @zh 当前 Y(可选,默认使用 positionY)@en Current Y (optional, defaults to positionY)\n */\n setPreferredVelocityTowards(\n targetX: number,\n targetY: number,\n currentX?: number,\n currentY?: number\n ): void {\n const x = currentX ?? this.positionX;\n const y = currentY ?? this.positionY;\n\n const dx = targetX - x;\n const dy = targetY - y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist > 0.0001) {\n const speed = Math.min(this.maxSpeed, dist);\n this.preferredVelocityX = (dx / dist) * speed;\n this.preferredVelocityY = (dy / dist) * speed;\n } else {\n this.preferredVelocityX = 0;\n this.preferredVelocityY = 0;\n }\n }\n\n /**\n * @zh 应用 ORCA 计算的新速度\n * @en Apply new velocity computed by ORCA\n */\n applyNewVelocity(): void {\n this.velocityX = this.newVelocityX;\n this.velocityY = this.newVelocityY;\n }\n\n /**\n * @zh 获取新速度的长度\n * @en Get length of new velocity\n */\n getNewSpeed(): number {\n return Math.sqrt(\n this.newVelocityX * this.newVelocityX +\n this.newVelocityY * this.newVelocityY\n );\n }\n\n /**\n * @zh 获取当前速度的长度\n * @en Get length of current velocity\n */\n getCurrentSpeed(): number {\n return Math.sqrt(\n this.velocityX * this.velocityX +\n this.velocityY * this.velocityY\n );\n }\n\n /**\n * @zh 停止代理\n * @en Stop the agent\n */\n stop(): void {\n this.velocityX = 0;\n this.velocityY = 0;\n this.preferredVelocityX = 0;\n this.preferredVelocityY = 0;\n this.newVelocityX = 0;\n this.newVelocityY = 0;\n }\n\n /**\n * @zh 重置组件状态\n * @en Reset component state\n */\n reset(): void {\n this.positionX = 0;\n this.positionY = 0;\n this.velocityX = 0;\n this.velocityY = 0;\n this.preferredVelocityX = 0;\n this.preferredVelocityY = 0;\n this.newVelocityX = 0;\n this.newVelocityY = 0;\n }\n\n /**\n * @zh 组件从实体移除时调用\n * @en Called when component is removed from entity\n */\n public onRemovedFromEntity(): void {\n this.reset();\n }\n}\n","/**\n * @zh 避让世界组件\n * @en Avoidance World Component\n */\n\nimport {\n Component,\n ECSComponent,\n Serializable,\n Serialize,\n Property\n} from '@esengine/ecs-framework';\nimport type { IObstacle, IORCASolverConfig } from '../avoidance/ILocalAvoidance';\nimport { DEFAULT_ORCA_CONFIG } from '../avoidance/ILocalAvoidance';\nimport type { ORCASolver } from '../avoidance/ORCASolver';\nimport type { KDTree } from '../avoidance/KDTree';\n\n// =============================================================================\n// 避让世界组件 | Avoidance World Component\n// =============================================================================\n\n/**\n * @zh 避让世界组件\n * @en Avoidance World Component\n *\n * @zh 挂载在场景实体上,持有 ORCA 求解器和静态障碍物\n * @en Attached to scene entity, holds ORCA solver and static obstacles\n *\n * @example\n * ```typescript\n * const worldEntity = scene.createEntity('AvoidanceWorld');\n * const world = worldEntity.addComponent(new AvoidanceWorldComponent());\n *\n * // 添加静态障碍物(墙壁)\n * world.addObstacle({\n * vertices: [\n * { x: 0, y: 0 },\n * { x: 10, y: 0 },\n * { x: 10, y: 1 },\n * { x: 0, y: 1 }\n * ]\n * });\n *\n * // LocalAvoidanceSystem 会自动使用此组件\n * ```\n */\n@ECSComponent('AvoidanceWorld')\n@Serializable({ version: 1, typeId: 'AvoidanceWorld' })\nexport class AvoidanceWorldComponent extends Component {\n // =========================================================================\n // ORCA 配置 | ORCA Configuration\n // =========================================================================\n\n /**\n * @zh 默认时间视野(代理)\n * @en Default time horizon for agents\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Horizon', min: 0.1, max: 10 })\n defaultTimeHorizon: number = DEFAULT_ORCA_CONFIG.defaultTimeHorizon;\n\n /**\n * @zh 默认时间视野(障碍物)\n * @en Default time horizon for obstacles\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Horizon Obst', min: 0.1, max: 10 })\n defaultTimeHorizonObst: number = DEFAULT_ORCA_CONFIG.defaultTimeHorizonObst;\n\n /**\n * @zh 时间步长\n * @en Time step\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Step', min: 0.001, max: 0.1 })\n timeStep: number = DEFAULT_ORCA_CONFIG.timeStep;\n\n // =========================================================================\n // 运行时实例(不序列化)| Runtime Instances (not serialized)\n // =========================================================================\n\n /**\n * @zh ORCA 求解器实例\n * @en ORCA solver instance\n */\n solver: ORCASolver | null = null;\n\n /**\n * @zh KD-Tree 实例\n * @en KD-Tree instance\n */\n kdTree: KDTree | null = null;\n\n /**\n * @zh 静态障碍物列表\n * @en List of static obstacles\n */\n obstacles: IObstacle[] = [];\n\n /**\n * @zh 是否已初始化\n * @en Whether initialized\n */\n initialized: boolean = false;\n\n // =========================================================================\n // 统计信息 | Statistics\n // =========================================================================\n\n /**\n * @zh 当前代理数量\n * @en Current agent count\n */\n agentCount: number = 0;\n\n /**\n * @zh 本帧处理的代理数\n * @en Agents processed this frame\n */\n agentsProcessedThisFrame: number = 0;\n\n /**\n * @zh 本帧 ORCA 计算耗时(毫秒)\n * @en ORCA computation time this frame (ms)\n */\n computeTimeMs: number = 0;\n\n // =========================================================================\n // 公共方法 | Public Methods\n // =========================================================================\n\n /**\n * @zh 获取 ORCA 配置\n * @en Get ORCA configuration\n */\n getConfig(): IORCASolverConfig {\n return {\n defaultTimeHorizon: this.defaultTimeHorizon,\n defaultTimeHorizonObst: this.defaultTimeHorizonObst,\n timeStep: this.timeStep\n };\n }\n\n /**\n * @zh 添加静态障碍物\n * @en Add static obstacle\n *\n * @param obstacle - @zh 障碍物(顶点列表,逆时针顺序)@en Obstacle (vertex list, counter-clockwise)\n */\n addObstacle(obstacle: IObstacle): void {\n this.obstacles.push(obstacle);\n }\n\n /**\n * @zh 添加矩形障碍物\n * @en Add rectangular obstacle\n *\n * @param x - @zh 左下角 X @en Bottom-left X\n * @param y - @zh 左下角 Y @en Bottom-left Y\n * @param width - @zh 宽度 @en Width\n * @param height - @zh 高度 @en Height\n */\n addRectObstacle(x: number, y: number, width: number, height: number): void {\n this.obstacles.push({\n vertices: [\n { x: x, y: y },\n { x: x + width, y: y },\n { x: x + width, y: y + height },\n { x: x, y: y + height }\n ]\n });\n }\n\n /**\n * @zh 移除所有障碍物\n * @en Remove all obstacles\n */\n clearObstacles(): void {\n this.obstacles = [];\n }\n\n /**\n * @zh 重置统计信息\n * @en Reset statistics\n */\n resetStats(): void {\n this.agentsProcessedThisFrame = 0;\n this.computeTimeMs = 0;\n }\n\n /**\n * @zh 组件从实体移除时调用\n * @en Called when component is removed from entity\n */\n public onRemovedFromEntity(): void {\n this.solver = null;\n this.kdTree = null;\n this.obstacles = [];\n this.initialized = false;\n }\n}\n","/**\n * @zh 局部避让系统\n * @en Local Avoidance System\n */\n\nimport {\n EntitySystem,\n Matcher,\n ECSSystem,\n type Entity\n} from '@esengine/ecs-framework';\nimport { AvoidanceAgentComponent } from './AvoidanceAgentComponent';\nimport { AvoidanceWorldComponent } from './AvoidanceWorldComponent';\nimport { PathfindingAgentComponent } from './PathfindingAgentComponent';\nimport { ORCASolver, createORCASolver } from '../avoidance/ORCASolver';\nimport { KDTree, createKDTree } from '../avoidance/KDTree';\nimport type { IAvoidanceAgent } from '../avoidance/ILocalAvoidance';\n\n// =============================================================================\n// 局部避让系统 | Local Avoidance System\n// =============================================================================\n\n/**\n * @zh 局部避让系统\n * @en Local Avoidance System\n *\n * @zh 使用 ORCA 算法计算代理的避让速度\n * @en Uses ORCA algorithm to compute avoidance velocities for agents\n *\n * @example\n * ```typescript\n * // 添加系统到场景\n * scene.addSystem(new LocalAvoidanceSystem());\n *\n * // 创建避让世界(可选,用于静态障碍物)\n * const worldEntity = scene.createEntity('AvoidanceWorld');\n * worldEntity.addComponent(new AvoidanceWorldComponent());\n *\n * // 创建代理\n * const agent = scene.createEntity('Agent');\n * const avoidance = agent.addComponent(new AvoidanceAgentComponent());\n *\n * // 可选:同时添加寻路代理,系统会自动同步位置\n * agent.addComponent(new PathfindingAgentComponent());\n *\n * // 每帧设置首选速度(朝向目标)\n * avoidance.setPreferredVelocityTowards(targetX, targetY);\n *\n * // 系统计算后,newVelocity 会被更新\n * // 如果 autoApplyVelocity = true,velocity 也会自动更新\n * ```\n */\n@ECSSystem('LocalAvoidance', { updateOrder: 50 })\nexport class LocalAvoidanceSystem extends EntitySystem {\n private worldEntity: Entity | null = null;\n private worldComponent: AvoidanceWorldComponent | null = null;\n\n private solver: ORCASolver | null = null;\n private kdTree: KDTree | null = null;\n\n constructor() {\n super(Matcher.all(AvoidanceAgentComponent));\n }\n\n /**\n * @zh 系统初始化\n * @en System initialization\n */\n protected onInitialize(): void {\n this.findWorldEntity();\n this.initializeSolver();\n }\n\n /**\n * @zh 系统激活时调用\n * @en Called when system is enabled\n */\n protected onEnable(): void {\n this.findWorldEntity();\n }\n\n /**\n * @zh 处理实体\n * @en Process entities\n */\n protected process(entities: readonly Entity[]): void {\n if (entities.length === 0) return;\n\n // 确保求解器已初始化\n if (!this.solver) {\n this.initializeSolver();\n }\n\n const startTime = performance.now();\n\n // 1. 收集所有代理数据\n const agents = this.collectAgentData(entities);\n\n // 2. 构建 KD-Tree\n this.kdTree!.build(agents);\n\n // 3. 获取静态障碍物\n const obstacles = this.worldComponent?.obstacles ?? [];\n\n // 4. 计算每个代理的新速度\n const deltaTime = this.worldComponent?.timeStep ?? (1 / 60);\n\n for (let i = 0; i < entities.length; i++) {\n const entity = entities[i]!;\n const component = entity.getComponent(AvoidanceAgentComponent)!;\n\n if (!component.enabled) continue;\n\n const agent = agents[i]!;\n\n // 查询邻居\n const neighborResults = this.kdTree!.queryNeighbors(\n agent.position,\n agent.neighborDist,\n agent.maxNeighbors,\n agent.id\n );\n\n const neighbors = neighborResults.map(r => r.agent);\n\n // ORCA 求解\n const newVelocity = this.solver!.computeNewVelocity(\n agent,\n neighbors,\n obstacles,\n deltaTime\n );\n\n // 更新组件\n component.newVelocityX = newVelocity.x;\n component.newVelocityY = newVelocity.y;\n\n // 自动应用新速度\n if (component.autoApplyVelocity) {\n component.applyNewVelocity();\n }\n }\n\n // 更新统计信息\n const endTime = performance.now();\n if (this.worldComponent) {\n this.worldComponent.agentCount = entities.length;\n this.worldComponent.agentsProcessedThisFrame = entities.length;\n this.worldComponent.computeTimeMs = endTime - startTime;\n }\n }\n\n // =========================================================================\n // 私有方法 | Private Methods\n // =========================================================================\n\n /**\n * @zh 查找世界实体\n * @en Find world entity\n */\n private findWorldEntity(): void {\n if (!this.scene) return;\n\n const entities = this.scene.entities.findEntitiesWithComponent(AvoidanceWorldComponent);\n if (entities.length > 0) {\n const entity = entities[0]!;\n const worldComp = entity.getComponent(AvoidanceWorldComponent);\n if (worldComp) {\n this.worldEntity = entity;\n this.worldComponent = worldComp;\n\n // 共享求解器和 KD-Tree 到世界组件\n if (this.solver && !worldComp.solver) {\n worldComp.solver = this.solver;\n }\n if (this.kdTree && !worldComp.kdTree) {\n worldComp.kdTree = this.kdTree;\n }\n worldComp.initialized = true;\n }\n }\n }\n\n /**\n * @zh 初始化求解器\n * @en Initialize solver\n */\n private initializeSolver(): void {\n const config = this.worldComponent?.getConfig();\n this.solver = createORCASolver(config);\n this.kdTree = createKDTree();\n\n if (this.worldComponent) {\n this.worldComponent.solver = this.solver;\n this.worldComponent.kdTree = this.kdTree;\n }\n }\n\n /**\n * @zh 收集代理数据\n * @en Collect agent data\n */\n private collectAgentData(entities: readonly Entity[]): IAvoidanceAgent[] {\n const agents: IAvoidanceAgent[] = [];\n\n for (const entity of entities) {\n const avoidance = entity.getComponent(AvoidanceAgentComponent)!;\n\n // 尝试从 PathfindingAgent 同步位置\n const pathAgent = entity.getComponent(PathfindingAgentComponent);\n if (pathAgent) {\n avoidance.positionX = pathAgent.x;\n avoidance.positionY = pathAgent.y;\n\n // 如果有有效路径,自动设置首选速度\n const waypoint = pathAgent.getNextWaypoint();\n if (waypoint && avoidance.preferredVelocityX === 0 && avoidance.preferredVelocityY === 0) {\n avoidance.setPreferredVelocityTowards(waypoint.x, waypoint.y);\n }\n }\n\n agents.push({\n id: entity.id,\n position: {\n x: avoidance.positionX,\n y: avoidance.positionY\n },\n velocity: {\n x: avoidance.velocityX,\n y: avoidance.velocityY\n },\n preferredVelocity: {\n x: avoidance.preferredVelocityX,\n y: avoidance.preferredVelocityY\n },\n radius: avoidance.radius,\n maxSpeed: avoidance.maxSpeed,\n neighborDist: avoidance.neighborDist,\n maxNeighbors: avoidance.maxNeighbors,\n timeHorizon: avoidance.timeHorizon,\n timeHorizonObst: avoidance.timeHorizonObst\n });\n }\n\n return agents;\n }\n}\n"],"mappings":"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","names":["Component","ECSComponent","Serializable","Serialize","Property","PathfindingAgentComponent","Component","x","y","targetX","targetY","hasRequest","priority","maxIterationsPerFrame","enableDynamicReplan","lookaheadDistance","validationInterval","state","PathfindingState","Idle","currentRequestId","path","pathIndex","pathCost","progress","lastValidationFrame","onPathComplete","onPathProgress","requestPathTo","cancelPath","Cancelled","getNextWaypoint","length","advanceWaypoint","isPathComplete","isSearching","InProgress","hasValidPath","Completed","getRemainingWaypointCount","Math","max","getPathLength","reset","onRemovedFromEntity","undefined","type","label","min","version","typeId","Component","ECSComponent","Serializable","Serialize","Property","PathfindingMapComponent","Component","mapType","width","height","allowDiagonal","avoidCorners","maxAgentsPerFrame","iterationsBudget","enableSmoothing","smoothingType","enableCache","cacheMaxEntries","cacheTtlMs","debugMode","showGrid","showPaths","map","pathfinder","smoother","initialized","activeRequests","iterationsUsedThisFrame","agentsProcessedThisFrame","cacheHits","cacheMisses","setWalkable","x","y","walkable","notifyObstacleChange","setRectWalkable","rectWidth","rectHeight","isWalkable","resetStats","getRemainingBudget","Math","max","getCacheStats","pathfinderWithCache","stats","hits","misses","enabled","hitRate","onRemovedFromEntity","clear","type","label","options","value","min","version","typeId","EntitySystem","Matcher","ECSSystem","PathfindingSystem","EntitySystem","Matcher","all","PathfindingAgentComponent","mapEntity","mapComponent","pathValidator","agentQueue","frameCounter","PathValidator","onInitialize","findMapEntity","initializeMap","onEnable","process","entities","pathfinder","resetStats","buildAgentQueue","processAgentsWithBudget","validatePaths","scene","findEntitiesWithComponent","PathfindingMapComponent","length","entity","mapComp","getComponent","initialized","map","mapType","GridMap","width","height","allowDiagonal","avoidCorners","IncrementalAStarPathfinder","enableCache","cacheConfig","maxEntries","cacheMaxEntries","ttlMs","cacheTtlMs","smoother","enableSmoothing","smoothingType","CatmullRomSmoother","CombinedSmoother","LineOfSightSmoother","agent","hasRequest","state","PathfindingState","Idle","Completed","Cancelled","push","component","sort","a","b","priority","maxAgents","maxAgentsPerFrame","remainingBudget","iterationsBudget","agentsProcessed","startNewRequest","InProgress","iterations","Math","min","maxIterationsPerFrame","progress","step","currentRequestId","updateAgentFromProgress","nodesSearched","iterationsUsedThisFrame","agentsProcessedThisFrame","cancel","cleanup","request","requestPath","x","y","targetX","targetY","id","path","pathIndex","activeRequests","estimatedProgress","onPathProgress","result","getResult","found","smooth","pathCost","cost","onPathComplete","Failed","enableDynamicReplan","isPathComplete","lastValidationFrame","validationInterval","checkEnd","lookaheadDistance","validatePath","valid","requestPathTo","updateOrder","Component","ECSComponent","Serializable","Serialize","Property","AvoidanceAgentComponent","Component","radius","DEFAULT_AGENT_PARAMS","maxSpeed","neighborDist","maxNeighbors","timeHorizon","timeHorizonObst","positionX","positionY","velocityX","velocityY","preferredVelocityX","preferredVelocityY","newVelocityX","newVelocityY","enabled","autoApplyVelocity","setPosition","x","y","setVelocity","setPreferredVelocity","setPreferredVelocityTowards","targetX","targetY","currentX","currentY","dx","dy","dist","Math","sqrt","speed","min","applyNewVelocity","getNewSpeed","getCurrentSpeed","stop","reset","onRemovedFromEntity","type","label","max","version","typeId","Component","ECSComponent","Serializable","Serialize","Property","AvoidanceWorldComponent","Component","defaultTimeHorizon","DEFAULT_ORCA_CONFIG","defaultTimeHorizonObst","timeStep","solver","kdTree","obstacles","initialized","agentCount","agentsProcessedThisFrame","computeTimeMs","getConfig","addObstacle","obstacle","push","addRectObstacle","x","y","width","height","vertices","clearObstacles","resetStats","onRemovedFromEntity","type","label","min","max","version","typeId","EntitySystem","Matcher","ECSSystem","LocalAvoidanceSystem","EntitySystem","Matcher","all","AvoidanceAgentComponent","worldEntity","worldComponent","solver","kdTree","onInitialize","findWorldEntity","initializeSolver","onEnable","process","entities","length","startTime","performance","now","agents","collectAgentData","build","obstacles","deltaTime","timeStep","i","entity","component","getComponent","enabled","agent","neighborResults","queryNeighbors","position","neighborDist","maxNeighbors","id","neighbors","map","r","newVelocity","computeNewVelocity","newVelocityX","x","newVelocityY","y","autoApplyVelocity","applyNewVelocity","endTime","agentCount","agentsProcessedThisFrame","computeTimeMs","scene","findEntitiesWithComponent","AvoidanceWorldComponent","worldComp","initialized","config","getConfig","createORCASolver","createKDTree","avoidance","pathAgent","PathfindingAgentComponent","positionX","positionY","waypoint","getNextWaypoint","preferredVelocityX","preferredVelocityY","setPreferredVelocityTowards","push","velocity","velocityX","velocityY","preferredVelocity","radius","maxSpeed","timeHorizon","timeHorizonObst","updateOrder"]}
|
|
1
|
+
{"version":3,"sources":["../src/ecs/NavigationAgentComponent.ts","../src/ecs/NavigationSystem.ts","../src/ecs/ORCAConfigComponent.ts"],"sourcesContent":["/**\n * @zh 统一导航代理组件\n * @en Unified Navigation Agent Component\n *\n * @zh 算法无关的通用导航属性,配合 NavigationSystem 使用\n * @en Algorithm-agnostic common navigation properties, works with NavigationSystem\n */\n\nimport {\n Component,\n ECSComponent,\n Serializable,\n Serialize,\n Property\n} from '@esengine/ecs-framework';\nimport type { IVector2 } from '../interfaces/IPathPlanner';\n\n/**\n * @zh 导航状态\n * @en Navigation state\n */\nexport enum NavigationState {\n /**\n * @zh 空闲\n * @en Idle\n */\n Idle = 'idle',\n\n /**\n * @zh 正在导航\n * @en Navigating\n */\n Navigating = 'navigating',\n\n /**\n * @zh 已到达\n * @en Arrived\n */\n Arrived = 'arrived',\n\n /**\n * @zh 路径被阻挡\n * @en Path blocked\n */\n Blocked = 'blocked',\n\n /**\n * @zh 无法到达\n * @en Unreachable\n */\n Unreachable = 'unreachable'\n}\n\n/**\n * @zh 统一导航代理组件\n * @en Unified navigation agent component\n *\n * @zh 包含算法无关的通用导航属性,不包含算法特定参数\n * @en Contains algorithm-agnostic common navigation properties, no algorithm-specific parameters\n *\n * @example\n * ```typescript\n * const entity = scene.createEntity('Agent');\n *\n * // 添加导航代理组件\n * const nav = entity.addComponent(new NavigationAgentComponent());\n * nav.radius = 0.5;\n * nav.maxSpeed = 5.0;\n *\n * // 设置目标\n * nav.setDestination(100, 200);\n *\n * // NavigationSystem 会自动处理:\n * // 1. 使用 IPathPlanner 计算全局路径\n * // 2. 使用 ILocalAvoidance 进行局部避让\n * // 3. 使用 ICollisionResolver 防止穿透\n * ```\n */\n@ECSComponent('NavigationAgent')\n@Serializable({ version: 1, typeId: 'NavigationAgent' })\nexport class NavigationAgentComponent extends Component {\n // =========================================================================\n // 核心物理属性 | Core Physical Properties\n // =========================================================================\n\n /**\n * @zh 代理半径\n * @en Agent radius\n */\n @Serialize()\n @Property({ type: 'number', label: 'Radius', min: 0.1, max: 10 })\n radius: number = 0.5;\n\n /**\n * @zh 最大速度\n * @en Maximum speed\n */\n @Serialize()\n @Property({ type: 'number', label: 'Max Speed', min: 0.1, max: 100 })\n maxSpeed: number = 5.0;\n\n /**\n * @zh 加速度(用于平滑移动)\n * @en Acceleration (for smooth movement)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Acceleration', min: 0.1, max: 100 })\n acceleration: number = 10.0;\n\n // =========================================================================\n // 寻路配置 | Pathfinding Configuration\n // =========================================================================\n\n /**\n * @zh 路径点到达阈值\n * @en Waypoint arrival threshold\n */\n @Serialize()\n @Property({ type: 'number', label: 'Waypoint Threshold', min: 0.1, max: 10 })\n waypointThreshold: number = 0.5;\n\n /**\n * @zh 目标到达阈值\n * @en Destination arrival threshold\n */\n @Serialize()\n @Property({ type: 'number', label: 'Arrival Threshold', min: 0.1, max: 10 })\n arrivalThreshold: number = 0.3;\n\n /**\n * @zh 路径重新计算间隔(秒)\n * @en Path recalculation interval (seconds)\n */\n @Serialize()\n @Property({ type: 'number', label: 'Repath Interval', min: 0.1, max: 10 })\n repathInterval: number = 0.5;\n\n // =========================================================================\n // 配置选项 | Configuration Options\n // =========================================================================\n\n /**\n * @zh 是否启用导航\n * @en Whether navigation is enabled\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Enabled' })\n enabled: boolean = true;\n\n /**\n * @zh 是否自动重新计算被阻挡的路径\n * @en Whether to auto repath when blocked\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Auto Repath' })\n autoRepath: boolean = true;\n\n /**\n * @zh 是否启用平滑转向\n * @en Whether to enable smooth steering\n */\n @Serialize()\n @Property({ type: 'boolean', label: 'Smooth Steering' })\n smoothSteering: boolean = true;\n\n // =========================================================================\n // 运行时状态 | Runtime State (Non-serialized)\n // =========================================================================\n\n /**\n * @zh 当前位置\n * @en Current position\n */\n position: IVector2 = { x: 0, y: 0 };\n\n /**\n * @zh 当前速度\n * @en Current velocity\n */\n velocity: IVector2 = { x: 0, y: 0 };\n\n /**\n * @zh 目标位置\n * @en Destination position\n */\n destination: IVector2 | null = null;\n\n /**\n * @zh 当前导航状态\n * @en Current navigation state\n */\n state: NavigationState = NavigationState.Idle;\n\n /**\n * @zh 当前路径\n * @en Current path\n */\n path: IVector2[] = [];\n\n /**\n * @zh 当前路径点索引\n * @en Current waypoint index\n */\n currentWaypointIndex: number = 0;\n\n /**\n * @zh 上次重新计算路径的时间\n * @en Last repath time\n */\n lastRepathTime: number = 0;\n\n // =========================================================================\n // 增量寻路状态(时间切片)| Incremental Pathfinding State (Time Slicing)\n // =========================================================================\n\n /**\n * @zh 当前增量寻路请求 ID\n * @en Current incremental pathfinding request ID\n */\n currentRequestId: number = -1;\n\n /**\n * @zh 寻路进度 (0-1)\n * @en Pathfinding progress (0-1)\n */\n pathProgress: number = 0;\n\n /**\n * @zh 优先级(数字越小优先级越高)\n * @en Priority (lower number = higher priority)\n */\n priority: number = 50;\n\n /**\n * @zh 是否正在等待路径计算完成\n * @en Whether waiting for path computation to complete\n */\n isComputingPath: boolean = false;\n\n // =========================================================================\n // 公共方法 | Public Methods\n // =========================================================================\n\n /**\n * @zh 设置位置\n * @en Set position\n *\n * @param x - @zh X 坐标 @en X coordinate\n * @param y - @zh Y 坐标 @en Y coordinate\n */\n setPosition(x: number, y: number): void {\n this.position = { x, y };\n }\n\n /**\n * @zh 设置目标位置\n * @en Set destination\n *\n * @param x - @zh 目标 X 坐标 @en Destination X coordinate\n * @param y - @zh 目标 Y 坐标 @en Destination Y coordinate\n */\n setDestination(x: number, y: number): void {\n this.destination = { x, y };\n this.state = NavigationState.Navigating;\n this.path = [];\n this.currentWaypointIndex = 0;\n this.lastRepathTime = 0;\n }\n\n /**\n * @zh 停止导航\n * @en Stop navigation\n */\n stop(): void {\n this.destination = null;\n this.state = NavigationState.Idle;\n this.path = [];\n this.currentWaypointIndex = 0;\n this.velocity = { x: 0, y: 0 };\n }\n\n /**\n * @zh 获取当前路径点\n * @en Get current waypoint\n *\n * @returns @zh 当前路径点,如果没有则返回 null @en Current waypoint, or null if none\n */\n getCurrentWaypoint(): IVector2 | null {\n if (this.currentWaypointIndex < this.path.length) {\n return this.path[this.currentWaypointIndex]!;\n }\n return null;\n }\n\n /**\n * @zh 获取到目标的距离\n * @en Get distance to destination\n *\n * @returns @zh 到目标的距离,如果没有目标则返回 Infinity @en Distance to destination, or Infinity if no destination\n */\n getDistanceToDestination(): number {\n if (!this.destination) return Infinity;\n const dx = this.destination.x - this.position.x;\n const dy = this.destination.y - this.position.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * @zh 获取当前速度大小\n * @en Get current speed\n *\n * @returns @zh 当前速度大小 @en Current speed magnitude\n */\n getCurrentSpeed(): number {\n return Math.sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y);\n }\n\n /**\n * @zh 检查是否已到达目标\n * @en Check if arrived at destination\n *\n * @returns @zh 是否已到达 @en Whether arrived\n */\n hasArrived(): boolean {\n return this.state === NavigationState.Arrived;\n }\n\n /**\n * @zh 检查路径是否被阻挡\n * @en Check if path is blocked\n *\n * @returns @zh 是否被阻挡 @en Whether blocked\n */\n isBlocked(): boolean {\n return this.state === NavigationState.Blocked;\n }\n\n /**\n * @zh 检查目标是否无法到达\n * @en Check if destination is unreachable\n *\n * @returns @zh 是否无法到达 @en Whether unreachable\n */\n isUnreachable(): boolean {\n return this.state === NavigationState.Unreachable;\n }\n\n /**\n * @zh 重置组件状态\n * @en Reset component state\n */\n reset(): void {\n this.position = { x: 0, y: 0 };\n this.velocity = { x: 0, y: 0 };\n this.destination = null;\n this.state = NavigationState.Idle;\n this.path = [];\n this.currentWaypointIndex = 0;\n this.lastRepathTime = 0;\n }\n\n /**\n * @zh 组件从实体移除时调用\n * @en Called when component is removed from entity\n */\n public onRemovedFromEntity(): void {\n this.reset();\n }\n}\n","/**\n * @zh 统一导航系统\n * @en Unified Navigation System\n *\n * @zh 可插拔的导航系统,支持运行时切换寻路、避让、碰撞检测算法\n * @en Pluggable navigation system, supports runtime swapping of pathfinding, avoidance, and collision detection algorithms\n */\n\nimport {\n EntitySystem,\n Matcher,\n ECSSystem,\n type Entity\n} from '@esengine/ecs-framework';\nimport { NavigationAgentComponent, NavigationState } from './NavigationAgentComponent';\nimport type {\n IPathPlanner,\n IVector2,\n IIncrementalPathPlanner,\n PathPlanState\n} from '../interfaces/IPathPlanner';\nimport { isIncrementalPlanner, PathPlanState as PlanState } from '../interfaces/IPathPlanner';\nimport type { ILocalAvoidance, IObstacleData, IAvoidanceAgentData } from '../interfaces/ILocalAvoidance';\nimport type { ICollisionResolver } from '../interfaces/ICollisionResolver';\nimport type { IFlowController, IFlowAgentData } from '../interfaces/IFlowController';\nimport { PassPermission } from '../interfaces/IFlowController';\n\n/**\n * @zh 导航系统配置\n * @en Navigation system configuration\n */\nexport interface INavigationSystemConfig {\n /**\n * @zh 时间步长\n * @en Time step\n */\n timeStep?: number;\n\n /**\n * @zh 是否启用路径规划阶段\n * @en Whether to enable path planning stage\n */\n enablePathPlanning?: boolean;\n\n /**\n * @zh 是否启用流量控制阶段\n * @en Whether to enable flow control stage\n */\n enableFlowControl?: boolean;\n\n /**\n * @zh 是否启用局部避让阶段\n * @en Whether to enable local avoidance stage\n */\n enableLocalAvoidance?: boolean;\n\n /**\n * @zh 是否启用碰撞解决阶段\n * @en Whether to enable collision resolution stage\n */\n enableCollisionResolution?: boolean;\n\n /**\n * @zh 是否启用代理间碰撞解决\n * @en Whether to enable agent-agent collision resolution\n */\n enableAgentCollisionResolution?: boolean;\n\n // =========================================================================\n // 时间切片配置 | Time Slicing Configuration\n // =========================================================================\n\n /**\n * @zh 是否启用时间切片寻路(需要 IIncrementalPathPlanner)\n * @en Whether to enable time-sliced pathfinding (requires IIncrementalPathPlanner)\n *\n * @zh 启用后,寻路计算会分散到多帧执行,避免卡顿\n * @en When enabled, pathfinding computation is spread across multiple frames to avoid stuttering\n */\n enableTimeSlicing?: boolean;\n\n /**\n * @zh 每帧总迭代预算\n * @en Total iteration budget per frame\n *\n * @zh 所有代理共享此预算,根据优先级分配\n * @en All agents share this budget, allocated by priority\n *\n * @default 1000\n */\n iterationsBudget?: number;\n\n /**\n * @zh 每帧最大处理代理数\n * @en Maximum agents to process per frame\n *\n * @zh 限制每帧处理的代理数量,避免过载\n * @en Limits the number of agents processed per frame to avoid overload\n *\n * @default 10\n */\n maxAgentsPerFrame?: number;\n\n /**\n * @zh 每个代理每帧最大迭代数\n * @en Maximum iterations per agent per frame\n *\n * @default 200\n */\n maxIterationsPerAgent?: number;\n}\n\n/**\n * @zh 默认配置\n * @en Default configuration\n */\nconst DEFAULT_CONFIG: Required<INavigationSystemConfig> = {\n timeStep: 1 / 60,\n enablePathPlanning: true,\n enableFlowControl: true,\n enableLocalAvoidance: true,\n enableCollisionResolution: true,\n enableAgentCollisionResolution: true,\n enableTimeSlicing: false,\n iterationsBudget: 1000,\n maxAgentsPerFrame: 10,\n maxIterationsPerAgent: 200\n};\n\n/**\n * @zh 统一导航系统\n * @en Unified Navigation System\n *\n * @zh 可插拔的导航系统,处理管线:PathPlanning → LocalAvoidance → CollisionResolution\n * @en Pluggable navigation system, pipeline: PathPlanning → LocalAvoidance → CollisionResolution\n *\n * @example\n * ```typescript\n * import {\n * NavigationSystem,\n * NavigationAgentComponent,\n * createNavMeshPathPlanner,\n * createORCAAvoidance,\n * createDefaultCollisionResolver\n * } from '@esengine/pathfinding/ecs';\n *\n * // 创建系统\n * const navSystem = new NavigationSystem();\n *\n * // 配置算法(可选,每个阶段都可以独立启用/禁用)\n * navSystem.setPathPlanner(createNavMeshPathPlanner(navMesh));\n * navSystem.setLocalAvoidance(createORCAAvoidance());\n * navSystem.setCollisionResolver(createDefaultCollisionResolver());\n *\n * scene.addSystem(navSystem);\n *\n * // 添加障碍物\n * navSystem.addObstacle({ vertices: [...] });\n *\n * // 创建代理\n * const agent = scene.createEntity('Agent');\n * const nav = agent.addComponent(new NavigationAgentComponent());\n * nav.setPosition(0, 0);\n * nav.setDestination(100, 100);\n *\n * // 运行时切换算法\n * navSystem.setPathPlanner(createJPSPlanner(gridMap));\n * navSystem.setLocalAvoidance(null); // 禁用避让\n * ```\n */\n@ECSSystem('Navigation', { updateOrder: 45 })\nexport class NavigationSystem extends EntitySystem {\n private config: Required<INavigationSystemConfig>;\n\n private pathPlanner: IPathPlanner | null = null;\n private flowController: IFlowController | null = null;\n private localAvoidance: ILocalAvoidance | null = null;\n private collisionResolver: ICollisionResolver | null = null;\n\n /**\n * @zh 静态障碍物(墙壁、建筑等)- 由 PathPlanner 和 CollisionResolver 处理\n * @en Static obstacles (walls, buildings) - handled by PathPlanner and CollisionResolver\n */\n private staticObstacles: IObstacleData[] = [];\n\n /**\n * @zh 动态障碍物(移动物体等)- 由 ORCA 和 CollisionResolver 处理\n * @en Dynamic obstacles (moving objects) - handled by ORCA and CollisionResolver\n */\n private dynamicObstacles: IObstacleData[] = [];\n\n private currentTime: number = 0;\n private agentEnterTimes: Map<number, number> = new Map();\n\n // =========================================================================\n // 时间切片状态 | Time Slicing State\n // =========================================================================\n\n /**\n * @zh 是否为增量寻路器\n * @en Whether the path planner is incremental\n */\n private isIncrementalPlanner: boolean = false;\n\n /**\n * @zh 等待寻路的代理队列(按优先级排序)\n * @en Queue of agents waiting for pathfinding (sorted by priority)\n */\n private pendingPathRequests: Set<number> = new Set();\n\n constructor(config: INavigationSystemConfig = {}) {\n super(Matcher.all(NavigationAgentComponent));\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n // =========================================================================\n // 算法设置 | Algorithm Setters\n // =========================================================================\n\n /**\n * @zh 设置路径规划器\n * @en Set path planner\n *\n * @param planner - @zh 路径规划器,传入 null 禁用路径规划 @en Path planner, pass null to disable\n *\n * @zh 如果传入 IIncrementalPathPlanner 且启用了时间切片,会自动使用增量寻路\n * @en If passing IIncrementalPathPlanner and time slicing is enabled, will automatically use incremental pathfinding\n *\n * @example\n * ```typescript\n * navSystem.setPathPlanner(createNavMeshPathPlanner(navMesh));\n * navSystem.setPathPlanner(createAStarPlanner(gridMap));\n * navSystem.setPathPlanner(createIncrementalAStarPlanner(gridMap)); // 支持时间切片\n * navSystem.setPathPlanner(null); // 禁用\n * ```\n */\n setPathPlanner(planner: IPathPlanner | null): void {\n this.pathPlanner?.dispose();\n this.pathPlanner = planner;\n this.isIncrementalPlanner = planner !== null && isIncrementalPlanner(planner);\n this.pendingPathRequests.clear();\n }\n\n /**\n * @zh 获取当前路径规划器\n * @en Get current path planner\n */\n getPathPlanner(): IPathPlanner | null {\n return this.pathPlanner;\n }\n\n /**\n * @zh 设置流量控制器\n * @en Set flow controller\n *\n * @param controller - @zh 流量控制器,传入 null 禁用流量控制 @en Flow controller, pass null to disable\n *\n * @example\n * ```typescript\n * navSystem.setFlowController(createFlowController());\n * navSystem.setFlowController(null); // 禁用\n * ```\n */\n setFlowController(controller: IFlowController | null): void {\n this.flowController?.dispose();\n this.flowController = controller;\n }\n\n /**\n * @zh 获取当前流量控制器\n * @en Get current flow controller\n */\n getFlowController(): IFlowController | null {\n return this.flowController;\n }\n\n /**\n * @zh 设置局部避让算法\n * @en Set local avoidance algorithm\n *\n * @param avoidance - @zh 局部避让算法,传入 null 禁用避让 @en Local avoidance, pass null to disable\n *\n * @example\n * ```typescript\n * navSystem.setLocalAvoidance(createORCAAvoidance());\n * navSystem.setLocalAvoidance(null); // 禁用\n * ```\n */\n setLocalAvoidance(avoidance: ILocalAvoidance | null): void {\n this.localAvoidance?.dispose();\n this.localAvoidance = avoidance;\n }\n\n /**\n * @zh 获取当前局部避让算法\n * @en Get current local avoidance algorithm\n */\n getLocalAvoidance(): ILocalAvoidance | null {\n return this.localAvoidance;\n }\n\n /**\n * @zh 设置碰撞解决器\n * @en Set collision resolver\n *\n * @param resolver - @zh 碰撞解决器,传入 null 禁用碰撞解决 @en Collision resolver, pass null to disable\n *\n * @example\n * ```typescript\n * navSystem.setCollisionResolver(createDefaultCollisionResolver());\n * navSystem.setCollisionResolver(null); // 禁用\n * ```\n */\n setCollisionResolver(resolver: ICollisionResolver | null): void {\n this.collisionResolver?.dispose();\n this.collisionResolver = resolver;\n }\n\n /**\n * @zh 获取当前碰撞解决器\n * @en Get current collision resolver\n */\n getCollisionResolver(): ICollisionResolver | null {\n return this.collisionResolver;\n }\n\n // =========================================================================\n // 障碍物管理 | Obstacle Management\n // =========================================================================\n\n /**\n * @zh 添加静态障碍物(墙壁、建筑等)\n * @en Add static obstacle (walls, buildings, etc.)\n *\n * @zh 静态障碍物由 PathPlanner 规划路径时考虑,CollisionResolver 防止穿透\n * @zh ORCA 不会处理静态障碍物,因为路径规划已经绑开了它们\n * @en Static obstacles are considered by PathPlanner for routing, CollisionResolver for penetration prevention\n * @en ORCA does NOT process static obstacles since path planning already avoids them\n *\n * @param obstacle - @zh 障碍物数据 @en Obstacle data\n */\n addStaticObstacle(obstacle: IObstacleData): void {\n this.staticObstacles.push(obstacle);\n }\n\n /**\n * @zh 添加动态障碍物(移动物体、临时障碍等)\n * @en Add dynamic obstacle (moving objects, temporary obstacles, etc.)\n *\n * @zh 动态障碍物由 ORCA 进行局部避让,CollisionResolver 防止穿透\n * @en Dynamic obstacles are handled by ORCA for local avoidance, CollisionResolver for penetration prevention\n *\n * @param obstacle - @zh 障碍物数据 @en Obstacle data\n */\n addDynamicObstacle(obstacle: IObstacleData): void {\n this.dynamicObstacles.push(obstacle);\n }\n\n /**\n * @zh 移除所有静态障碍物\n * @en Remove all static obstacles\n */\n clearStaticObstacles(): void {\n this.staticObstacles = [];\n }\n\n /**\n * @zh 移除所有动态障碍物\n * @en Remove all dynamic obstacles\n */\n clearDynamicObstacles(): void {\n this.dynamicObstacles = [];\n }\n\n /**\n * @zh 移除所有障碍物(静态和动态)\n * @en Remove all obstacles (static and dynamic)\n */\n clearObstacles(): void {\n this.staticObstacles = [];\n this.dynamicObstacles = [];\n }\n\n /**\n * @zh 获取静态障碍物列表\n * @en Get static obstacles list\n */\n getStaticObstacles(): readonly IObstacleData[] {\n return this.staticObstacles;\n }\n\n /**\n * @zh 获取动态障碍物列表\n * @en Get dynamic obstacles list\n */\n getDynamicObstacles(): readonly IObstacleData[] {\n return this.dynamicObstacles;\n }\n\n /**\n * @zh 获取所有障碍物列表(静态+动态)\n * @en Get all obstacles list (static + dynamic)\n */\n getObstacles(): readonly IObstacleData[] {\n return [...this.staticObstacles, ...this.dynamicObstacles];\n }\n\n /**\n * @zh 获取所有障碍物用于碰撞检测\n * @en Get all obstacles for collision detection\n */\n private getAllObstaclesForCollision(): readonly IObstacleData[] {\n return [...this.staticObstacles, ...this.dynamicObstacles];\n }\n\n /**\n * @zh 设置静态障碍物列表\n * @en Set static obstacles list\n *\n * @param obstacles - @zh 障碍物列表 @en Obstacles list\n */\n setStaticObstacles(obstacles: IObstacleData[]): void {\n this.staticObstacles = [...obstacles];\n }\n\n /**\n * @zh 设置动态障碍物列表\n * @en Set dynamic obstacles list\n *\n * @param obstacles - @zh 障碍物列表 @en Obstacles list\n */\n setDynamicObstacles(obstacles: IObstacleData[]): void {\n this.dynamicObstacles = [...obstacles];\n }\n\n // =========================================================================\n // 系统生命周期 | System Lifecycle\n // =========================================================================\n\n /**\n * @zh 系统销毁时调用\n * @en Called when system is destroyed\n */\n protected onDestroy(): void {\n this.pathPlanner?.dispose();\n this.flowController?.dispose();\n this.localAvoidance?.dispose();\n this.collisionResolver?.dispose();\n this.pathPlanner = null;\n this.flowController = null;\n this.localAvoidance = null;\n this.collisionResolver = null;\n this.staticObstacles = [];\n this.dynamicObstacles = [];\n this.agentEnterTimes.clear();\n this.pendingPathRequests.clear();\n this.isIncrementalPlanner = false;\n }\n\n // =========================================================================\n // 处理管线 | Processing Pipeline\n // =========================================================================\n\n /**\n * @zh 处理实体\n * @en Process entities\n */\n protected process(entities: readonly Entity[]): void {\n if (entities.length === 0) return;\n\n const deltaTime = this.config.timeStep;\n this.currentTime += deltaTime;\n\n const agentDataMap = new Map<number, IAvoidanceAgentData>();\n const flowAgentDataList: IFlowAgentData[] = [];\n const entityMap = new Map<number, Entity>();\n\n // Build entity map for quick lookup\n for (const entity of entities) {\n entityMap.set(entity.id, entity);\n }\n\n // Stage 1: Path Planning\n // 时间切片模式使用增量寻路,同步模式使用普通寻路\n // Time slicing mode uses incremental pathfinding, sync mode uses normal pathfinding\n if (this.config.enablePathPlanning && this.pathPlanner) {\n if (this.config.enableTimeSlicing && this.isIncrementalPlanner) {\n // Incremental mode: process all agents together with budget allocation\n const agentList: Array<{ entityId: number; agent: NavigationAgentComponent }> = [];\n for (const entity of entities) {\n const agent = entity.getComponent(NavigationAgentComponent)!;\n if (agent.enabled) {\n agentList.push({ entityId: entity.id, agent });\n }\n }\n this.processIncrementalPathPlanning(agentList, entityMap);\n } else {\n // Sync mode: process each agent individually\n for (const entity of entities) {\n const agent = entity.getComponent(NavigationAgentComponent)!;\n if (!agent.enabled) continue;\n this.processPathPlanning(agent, deltaTime);\n }\n }\n }\n\n // Stage 1b: Build Agent Data (after path planning)\n for (const entity of entities) {\n const agent = entity.getComponent(NavigationAgentComponent)!;\n if (!agent.enabled) continue;\n\n // Track enter time for flow control\n if (!this.agentEnterTimes.has(entity.id)) {\n this.agentEnterTimes.set(entity.id, this.currentTime);\n }\n\n // Calculate preferred velocity from path\n const preferredVelocity = this.calculatePreferredVelocity(agent);\n\n // Build agent data for avoidance\n const agentData = this.buildAgentData(entity, agent, preferredVelocity);\n agentDataMap.set(entity.id, agentData);\n\n // Build flow agent data\n flowAgentDataList.push({\n id: entity.id,\n position: { x: agent.position.x, y: agent.position.y },\n destination: agent.destination,\n currentWaypoint: agent.getCurrentWaypoint(),\n radius: agent.radius,\n priority: 50,\n enterTime: this.agentEnterTimes.get(entity.id)\n });\n }\n\n // Stage 2: Flow Control\n if (this.config.enableFlowControl && this.flowController) {\n this.flowController.update(flowAgentDataList, deltaTime);\n }\n\n // Stage 3: Local Avoidance (batch processing)\n // Only process agents that have permission to proceed or yield\n if (this.config.enableLocalAvoidance && this.localAvoidance && agentDataMap.size > 0) {\n const proceedingAgents: IAvoidanceAgentData[] = [];\n const proceedingEntityIds = new Set<number>();\n\n for (const entity of entities) {\n const agent = entity.getComponent(NavigationAgentComponent)!;\n if (!agent.enabled) continue;\n\n const flowResult = this.flowController?.getFlowControl(entity.id);\n const permission = flowResult?.permission ?? PassPermission.Proceed;\n\n if (permission === PassPermission.Wait) {\n // Agent must wait - move to wait position or stop\n this.handleWaitingAgent(entity, agent, flowResult!.waitPosition, deltaTime);\n } else {\n // Agent can proceed (with possible speed reduction for Yield)\n const agentData = agentDataMap.get(entity.id);\n if (agentData) {\n // Apply speed multiplier from flow control\n const speedMult = flowResult?.speedMultiplier ?? 1.0;\n if (speedMult < 1.0) {\n // Create modified agent data with scaled velocity\n const modifiedAgentData: IAvoidanceAgentData = {\n ...agentData,\n preferredVelocity: {\n x: agentData.preferredVelocity.x * speedMult,\n y: agentData.preferredVelocity.y * speedMult\n }\n };\n proceedingAgents.push(modifiedAgentData);\n } else {\n proceedingAgents.push(agentData);\n }\n proceedingEntityIds.add(entity.id);\n }\n }\n }\n\n // Compute avoidance only for proceeding agents\n // 关键:ORCA 只处理动态障碍物,静态障碍物由路径规划处理\n // Key: ORCA only handles dynamic obstacles, static obstacles are handled by path planning\n if (proceedingAgents.length > 0) {\n const avoidanceResults = this.localAvoidance.computeBatchAvoidance(\n proceedingAgents,\n this.dynamicObstacles,\n deltaTime\n );\n\n for (const entity of entities) {\n if (!proceedingEntityIds.has(entity.id)) continue;\n\n const agent = entity.getComponent(NavigationAgentComponent)!;\n if (!agent.enabled) continue;\n\n const result = avoidanceResults.get(entity.id);\n if (result) {\n this.applyAvoidanceResult(entity, agent, result.velocity, deltaTime);\n } else {\n const agentData = agentDataMap.get(entity.id);\n if (agentData) {\n this.applyAvoidanceResult(entity, agent, agentData.preferredVelocity, deltaTime);\n }\n }\n }\n }\n } else {\n // No avoidance, just use preferred velocity (but still respect flow control)\n for (const entity of entities) {\n const agent = entity.getComponent(NavigationAgentComponent)!;\n if (!agent.enabled) continue;\n\n const flowResult = this.flowController?.getFlowControl(entity.id);\n const permission = flowResult?.permission ?? PassPermission.Proceed;\n\n if (permission === PassPermission.Wait && this.config.enableFlowControl && this.flowController) {\n this.handleWaitingAgent(entity, agent, flowResult!.waitPosition, deltaTime);\n } else {\n const agentData = agentDataMap.get(entity.id);\n if (agentData) {\n let velocity = agentData.preferredVelocity;\n const speedMult = flowResult?.speedMultiplier ?? 1.0;\n if (speedMult < 1.0) {\n velocity = {\n x: velocity.x * speedMult,\n y: velocity.y * speedMult\n };\n }\n this.applyAvoidanceResult(entity, agent, velocity, deltaTime);\n }\n }\n }\n }\n\n // Stage 4: Agent-Agent Collision Resolution\n if (this.config.enableAgentCollisionResolution && this.collisionResolver) {\n this.resolveAgentCollisions(entities);\n }\n\n // Cleanup enter times for removed agents\n this.cleanupEnterTimes(entities);\n }\n\n /**\n * @zh 处理等待中的代理\n * @en Handle waiting agent\n */\n private handleWaitingAgent(\n entity: Entity,\n agent: NavigationAgentComponent,\n waitPosition: IVector2 | null,\n deltaTime: number\n ): void {\n if (waitPosition) {\n // Move towards wait position\n const dx = waitPosition.x - agent.position.x;\n const dy = waitPosition.y - agent.position.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist > agent.arrivalThreshold) {\n const speed = Math.min(agent.maxSpeed * 0.5, dist);\n const velocity: IVector2 = {\n x: (dx / dist) * speed,\n y: (dy / dist) * speed\n };\n this.applyAvoidanceResult(entity, agent, velocity, deltaTime);\n } else {\n // At wait position, stop\n agent.velocity = { x: 0, y: 0 };\n }\n } else {\n // No wait position specified, just stop\n agent.velocity = { x: 0, y: 0 };\n }\n }\n\n /**\n * @zh 清理已移除代理的进入时间记录\n * @en Cleanup enter times for removed agents\n */\n private cleanupEnterTimes(entities: readonly Entity[]): void {\n const activeIds = new Set(entities.map(e => e.id));\n for (const id of this.agentEnterTimes.keys()) {\n if (!activeIds.has(id)) {\n this.agentEnterTimes.delete(id);\n }\n }\n }\n\n /**\n * @zh 处理路径规划(同步模式)\n * @en Process path planning (synchronous mode)\n */\n private processPathPlanning(agent: NavigationAgentComponent, deltaTime: number): void {\n if (!agent.destination || agent.state === NavigationState.Arrived) {\n return;\n }\n\n const needsRepath =\n agent.path.length === 0 ||\n (agent.autoRepath &&\n this.currentTime - agent.lastRepathTime > agent.repathInterval);\n\n if (needsRepath && this.pathPlanner) {\n const result = this.pathPlanner.findPath(\n agent.position,\n agent.destination,\n { agentRadius: agent.radius }\n );\n\n if (result.found) {\n agent.path = result.path.map(p => ({ x: p.x, y: p.y }));\n agent.currentWaypointIndex = 0;\n agent.state = NavigationState.Navigating;\n } else {\n agent.state = NavigationState.Unreachable;\n agent.path = [];\n }\n\n agent.lastRepathTime = this.currentTime;\n }\n\n this.advanceWaypoint(agent);\n }\n\n /**\n * @zh 处理增量路径规划(时间切片模式)\n * @en Process incremental path planning (time slicing mode)\n *\n * @param agents - @zh 代理列表 @en Agent list\n * @param entityMap - @zh 实体 ID 到代理的映射 @en Entity ID to agent mapping\n */\n private processIncrementalPathPlanning(\n agents: Array<{ entityId: number; agent: NavigationAgentComponent }>,\n entityMap: Map<number, Entity>\n ): void {\n if (!this.pathPlanner || !this.isIncrementalPlanner) return;\n\n const planner = this.pathPlanner as IIncrementalPathPlanner;\n let remainingBudget = this.config.iterationsBudget;\n let processedCount = 0;\n\n // Step 1: Check which agents need path requests\n for (const { entityId, agent } of agents) {\n if (!agent.destination || agent.state === NavigationState.Arrived) {\n if (agent.currentRequestId >= 0) {\n planner.cleanup(agent.currentRequestId);\n agent.currentRequestId = -1;\n agent.isComputingPath = false;\n }\n continue;\n }\n\n const needsRepath =\n !agent.isComputingPath &&\n agent.currentRequestId < 0 &&\n (agent.path.length === 0 ||\n (agent.autoRepath &&\n this.currentTime - agent.lastRepathTime > agent.repathInterval));\n\n if (needsRepath) {\n this.pendingPathRequests.add(entityId);\n }\n }\n\n // Step 2: Start new requests for pending agents (limited by maxAgentsPerFrame)\n const pendingArray = Array.from(this.pendingPathRequests);\n\n // Sort by priority (lower number = higher priority)\n pendingArray.sort((a, b) => {\n const agentA = agents.find(x => x.entityId === a);\n const agentB = agents.find(x => x.entityId === b);\n return (agentA?.agent.priority ?? 50) - (agentB?.agent.priority ?? 50);\n });\n\n for (const entityId of pendingArray) {\n if (processedCount >= this.config.maxAgentsPerFrame) break;\n\n const entry = agents.find(x => x.entityId === entityId);\n const destination = entry?.agent.destination;\n if (!entry || !destination) {\n this.pendingPathRequests.delete(entityId);\n continue;\n }\n\n const { agent } = entry;\n\n // Start new request\n const request = planner.requestPath(\n agent.position,\n destination,\n { agentRadius: agent.radius }\n );\n\n agent.currentRequestId = request.id;\n agent.isComputingPath = true;\n agent.pathProgress = 0;\n this.pendingPathRequests.delete(entityId);\n processedCount++;\n }\n\n // Step 3: Process active requests\n const activeAgents = agents.filter(x => x.agent.isComputingPath && x.agent.currentRequestId >= 0);\n\n // Sort by priority\n activeAgents.sort((a, b) => a.agent.priority - b.agent.priority);\n\n for (const { agent } of activeAgents) {\n if (remainingBudget <= 0) break;\n\n const iterations = Math.min(remainingBudget, this.config.maxIterationsPerAgent);\n const progress = planner.step(agent.currentRequestId, iterations);\n\n remainingBudget -= progress.nodesSearched;\n agent.pathProgress = progress.estimatedProgress;\n\n if (progress.state === PlanState.Completed) {\n const result = planner.getResult(agent.currentRequestId);\n planner.cleanup(agent.currentRequestId);\n\n if (result && result.found) {\n agent.path = result.path.map(p => ({ x: p.x, y: p.y }));\n agent.currentWaypointIndex = 0;\n agent.state = NavigationState.Navigating;\n } else {\n agent.state = NavigationState.Unreachable;\n agent.path = [];\n }\n\n agent.currentRequestId = -1;\n agent.isComputingPath = false;\n agent.pathProgress = 0;\n agent.lastRepathTime = this.currentTime;\n } else if (progress.state === PlanState.Failed || progress.state === PlanState.Cancelled) {\n planner.cleanup(agent.currentRequestId);\n agent.state = NavigationState.Unreachable;\n agent.path = [];\n agent.currentRequestId = -1;\n agent.isComputingPath = false;\n agent.pathProgress = 0;\n agent.lastRepathTime = this.currentTime;\n }\n }\n\n // Step 4: Advance waypoints for all agents\n for (const { agent } of agents) {\n this.advanceWaypoint(agent);\n }\n }\n\n /**\n * @zh 推进路径点\n * @en Advance waypoint\n */\n private advanceWaypoint(agent: NavigationAgentComponent): void {\n while (agent.currentWaypointIndex < agent.path.length) {\n const waypoint = agent.path[agent.currentWaypointIndex]!;\n const dx = waypoint.x - agent.position.x;\n const dy = waypoint.y - agent.position.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < agent.waypointThreshold) {\n agent.currentWaypointIndex++;\n } else {\n break;\n }\n }\n }\n\n /**\n * @zh 计算首选速度\n * @en Calculate preferred velocity\n */\n private calculatePreferredVelocity(agent: NavigationAgentComponent): IVector2 {\n if (!agent.destination) {\n return { x: 0, y: 0 };\n }\n\n let targetX: number, targetY: number;\n let isLastWaypoint = false;\n\n if (agent.currentWaypointIndex < agent.path.length) {\n const waypoint = agent.path[agent.currentWaypointIndex]!;\n targetX = waypoint.x;\n targetY = waypoint.y;\n isLastWaypoint = agent.currentWaypointIndex === agent.path.length - 1;\n } else {\n targetX = agent.destination.x;\n targetY = agent.destination.y;\n isLastWaypoint = true;\n }\n\n const dx = targetX - agent.position.x;\n const dy = targetY - agent.position.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < 0.0001) {\n return { x: 0, y: 0 };\n }\n\n // 只在接近最终目标时减速,中间路径点保持全速\n // Only slow down when approaching final destination, keep full speed for intermediate waypoints\n const speed = isLastWaypoint ? Math.min(agent.maxSpeed, dist) : agent.maxSpeed;\n return {\n x: (dx / dist) * speed,\n y: (dy / dist) * speed\n };\n }\n\n /**\n * @zh 构建代理数据\n * @en Build agent data\n */\n private buildAgentData(\n entity: Entity,\n agent: NavigationAgentComponent,\n preferredVelocity: IVector2\n ): IAvoidanceAgentData {\n return {\n id: entity.id,\n position: { x: agent.position.x, y: agent.position.y },\n velocity: { x: agent.velocity.x, y: agent.velocity.y },\n preferredVelocity,\n radius: agent.radius,\n maxSpeed: agent.maxSpeed\n };\n }\n\n /**\n * @zh 应用避让结果\n * @en Apply avoidance result\n */\n private applyAvoidanceResult(\n entity: Entity,\n agent: NavigationAgentComponent,\n newVelocity: IVector2,\n deltaTime: number\n ): void {\n // CollisionResolver 处理所有障碍物(静态+动态)\n // CollisionResolver handles all obstacles (static + dynamic)\n const allObstacles = this.getAllObstaclesForCollision();\n\n // Stage 3a: Collision Resolution (velocity validation)\n if (this.config.enableCollisionResolution && this.collisionResolver && allObstacles.length > 0) {\n newVelocity = this.collisionResolver.validateVelocity(\n agent.position,\n newVelocity,\n agent.radius,\n allObstacles,\n deltaTime\n );\n }\n\n // Apply smooth steering if enabled\n if (agent.smoothSteering) {\n newVelocity = this.applySmoothSteering(agent, newVelocity, deltaTime);\n }\n\n // Update velocity\n agent.velocity = { x: newVelocity.x, y: newVelocity.y };\n\n // Calculate new position\n let newPosition: IVector2 = {\n x: agent.position.x + newVelocity.x * deltaTime,\n y: agent.position.y + newVelocity.y * deltaTime\n };\n\n // Stage 3b: Collision Resolution (position correction)\n if (this.config.enableCollisionResolution && this.collisionResolver && allObstacles.length > 0) {\n newPosition = this.collisionResolver.resolveCollision(\n newPosition,\n agent.radius,\n allObstacles\n );\n }\n\n // Update position\n agent.position = newPosition;\n\n // Check arrival\n this.checkArrival(agent);\n }\n\n /**\n * @zh 应用平滑转向\n * @en Apply smooth steering\n */\n private applySmoothSteering(\n agent: NavigationAgentComponent,\n targetVelocity: IVector2,\n deltaTime: number\n ): IVector2 {\n const maxChange = agent.acceleration * deltaTime;\n\n const dvx = targetVelocity.x - agent.velocity.x;\n const dvy = targetVelocity.y - agent.velocity.y;\n const changeMag = Math.sqrt(dvx * dvx + dvy * dvy);\n\n if (changeMag <= maxChange) {\n return targetVelocity;\n }\n\n const factor = maxChange / changeMag;\n const newVel = {\n x: agent.velocity.x + dvx * factor,\n y: agent.velocity.y + dvy * factor\n };\n\n // 保持目标速度大小,避免转弯时减速\n // Maintain target speed to prevent slowdown during turns\n const targetSpeed = Math.sqrt(targetVelocity.x * targetVelocity.x + targetVelocity.y * targetVelocity.y);\n const newSpeed = Math.sqrt(newVel.x * newVel.x + newVel.y * newVel.y);\n if (newSpeed > 0.0001 && targetSpeed > 0.0001) {\n const scale = targetSpeed / newSpeed;\n newVel.x *= scale;\n newVel.y *= scale;\n }\n\n return newVel;\n }\n\n /**\n * @zh 检查是否到达目标\n * @en Check if arrived at destination\n */\n private checkArrival(agent: NavigationAgentComponent): void {\n if (!agent.destination) return;\n\n const dx = agent.destination.x - agent.position.x;\n const dy = agent.destination.y - agent.position.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < agent.arrivalThreshold) {\n agent.state = NavigationState.Arrived;\n agent.velocity = { x: 0, y: 0 };\n }\n }\n\n /**\n * @zh 解决代理间碰撞\n * @en Resolve agent-agent collisions\n */\n private resolveAgentCollisions(entities: readonly Entity[]): void {\n if (!this.collisionResolver) return;\n\n const corrections: Map<number, IVector2> = new Map();\n const activeEntities = entities.filter(e => {\n const agent = e.getComponent(NavigationAgentComponent)!;\n return agent.enabled;\n });\n\n for (let i = 0; i < activeEntities.length; i++) {\n for (let j = i + 1; j < activeEntities.length; j++) {\n const entityA = activeEntities[i]!;\n const entityB = activeEntities[j]!;\n const agentA = entityA.getComponent(NavigationAgentComponent)!;\n const agentB = entityB.getComponent(NavigationAgentComponent)!;\n\n const collision = this.collisionResolver.detectAgentCollision(\n agentA.position,\n agentA.radius,\n agentB.position,\n agentB.radius\n );\n\n if (collision.collided) {\n const halfPush = (collision.penetration + 0.01) * 0.5;\n\n const corrA = corrections.get(entityA.id) ?? { x: 0, y: 0 };\n corrA.x += collision.normal.x * halfPush;\n corrA.y += collision.normal.y * halfPush;\n corrections.set(entityA.id, corrA);\n\n const corrB = corrections.get(entityB.id) ?? { x: 0, y: 0 };\n corrB.x -= collision.normal.x * halfPush;\n corrB.y -= collision.normal.y * halfPush;\n corrections.set(entityB.id, corrB);\n }\n }\n }\n\n const allObstacles = this.getAllObstaclesForCollision();\n\n for (const [entityId, correction] of corrections) {\n const entity = activeEntities.find(e => e.id === entityId);\n if (!entity) continue;\n\n const agent = entity.getComponent(NavigationAgentComponent)!;\n let newPosition: IVector2 = {\n x: agent.position.x + correction.x,\n y: agent.position.y + correction.y\n };\n\n if (allObstacles.length > 0) {\n newPosition = this.collisionResolver.resolveCollision(\n newPosition,\n agent.radius,\n allObstacles\n );\n }\n\n agent.position = newPosition;\n }\n }\n}\n","/**\n * @zh ORCA 算法配置组件\n * @en ORCA Algorithm Configuration Component\n *\n * @zh 可选组件,仅当使用 ORCA 避让算法时需要,用于覆盖默认 ORCA 参数\n * @en Optional component, only needed when using ORCA avoidance, to override default ORCA parameters\n */\n\nimport {\n Component,\n ECSComponent,\n Serializable,\n Serialize,\n Property\n} from '@esengine/ecs-framework';\n\n/**\n * @zh ORCA 算法配置组件\n * @en ORCA algorithm configuration component\n *\n * @zh 可选组件,附加到代理实体上以覆盖默认 ORCA 参数\n * @en Optional component, attach to agent entities to override default ORCA parameters\n *\n * @example\n * ```typescript\n * const entity = scene.createEntity('Agent');\n *\n * // 添加导航代理\n * entity.addComponent(new NavigationAgentComponent());\n *\n * // 可选:添加 ORCA 配置以自定义参数\n * const orcaConfig = entity.addComponent(new ORCAConfigComponent());\n * orcaConfig.timeHorizon = 3.0; // 更长的预测时间\n * orcaConfig.neighborDist = 20.0; // 更大的邻居检测范围\n * ```\n */\n@ECSComponent('ORCAConfig')\n@Serializable({ version: 1, typeId: 'ORCAConfig' })\nexport class ORCAConfigComponent extends Component {\n /**\n * @zh 邻居检测距离\n * @en Neighbor detection distance\n *\n * @zh 代理检测邻居的最大距离,更大的值意味着更早开始避让但也更消耗性能\n * @en Maximum distance for detecting neighbors, larger value means earlier avoidance but more performance cost\n */\n @Serialize()\n @Property({ type: 'number', label: 'Neighbor Dist', min: 1, max: 100 })\n neighborDist: number = 15.0;\n\n /**\n * @zh 最大邻居数量\n * @en Maximum number of neighbors\n *\n * @zh 计算避让时考虑的最大邻居数量,更多邻居意味着更精确但也更消耗性能\n * @en Maximum neighbors considered for avoidance, more neighbors means more accurate but slower\n */\n @Serialize()\n @Property({ type: 'number', label: 'Max Neighbors', min: 1, max: 50 })\n maxNeighbors: number = 10;\n\n /**\n * @zh 代理避让时间视野\n * @en Time horizon for agent avoidance\n *\n * @zh 预测其他代理未来位置的时间范围,更长意味着更平滑但可能过度避让\n * @en Time range for predicting other agents' future positions, longer means smoother but may over-avoid\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Horizon', min: 0.1, max: 10 })\n timeHorizon: number = 2.0;\n\n /**\n * @zh 障碍物避让时间视野\n * @en Time horizon for obstacle avoidance\n *\n * @zh 预测与障碍物碰撞的时间范围,通常比代理视野短\n * @en Time range for predicting obstacle collisions, usually shorter than agent horizon\n */\n @Serialize()\n @Property({ type: 'number', label: 'Time Horizon Obst', min: 0.1, max: 10 })\n timeHorizonObst: number = 1.0;\n}\n"],"mappings":"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","names":["Component","ECSComponent","Serializable","Serialize","Property","NavigationState","NavigationAgentComponent","Component","radius","maxSpeed","acceleration","waypointThreshold","arrivalThreshold","repathInterval","enabled","autoRepath","smoothSteering","position","x","y","velocity","destination","state","path","currentWaypointIndex","lastRepathTime","currentRequestId","pathProgress","priority","isComputingPath","setPosition","setDestination","stop","getCurrentWaypoint","length","getDistanceToDestination","Infinity","dx","dy","Math","sqrt","getCurrentSpeed","hasArrived","isBlocked","isUnreachable","reset","onRemovedFromEntity","type","label","min","max","version","typeId","EntitySystem","Matcher","ECSSystem","DEFAULT_CONFIG","timeStep","enablePathPlanning","enableFlowControl","enableLocalAvoidance","enableCollisionResolution","enableAgentCollisionResolution","enableTimeSlicing","iterationsBudget","maxAgentsPerFrame","maxIterationsPerAgent","NavigationSystem","EntitySystem","config","Matcher","all","NavigationAgentComponent","pathPlanner","flowController","localAvoidance","collisionResolver","staticObstacles","dynamicObstacles","currentTime","agentEnterTimes","Map","isIncrementalPlanner","pendingPathRequests","Set","setPathPlanner","planner","dispose","clear","getPathPlanner","setFlowController","controller","getFlowController","setLocalAvoidance","avoidance","getLocalAvoidance","setCollisionResolver","resolver","getCollisionResolver","addStaticObstacle","obstacle","push","addDynamicObstacle","clearStaticObstacles","clearDynamicObstacles","clearObstacles","getStaticObstacles","getDynamicObstacles","getObstacles","getAllObstaclesForCollision","setStaticObstacles","obstacles","setDynamicObstacles","onDestroy","process","entities","length","deltaTime","agentDataMap","flowAgentDataList","entityMap","entity","set","id","agentList","agent","getComponent","enabled","entityId","processIncrementalPathPlanning","processPathPlanning","has","preferredVelocity","calculatePreferredVelocity","agentData","buildAgentData","position","x","y","destination","currentWaypoint","getCurrentWaypoint","radius","priority","enterTime","get","update","size","proceedingAgents","proceedingEntityIds","flowResult","getFlowControl","permission","PassPermission","Proceed","Wait","handleWaitingAgent","waitPosition","speedMult","speedMultiplier","modifiedAgentData","add","avoidanceResults","computeBatchAvoidance","result","applyAvoidanceResult","velocity","resolveAgentCollisions","cleanupEnterTimes","dx","dy","dist","Math","sqrt","arrivalThreshold","speed","min","maxSpeed","activeIds","map","e","keys","delete","state","NavigationState","Arrived","needsRepath","path","autoRepath","lastRepathTime","repathInterval","findPath","agentRadius","found","p","currentWaypointIndex","Navigating","Unreachable","advanceWaypoint","agents","remainingBudget","processedCount","currentRequestId","cleanup","isComputingPath","pendingArray","Array","from","sort","a","b","agentA","find","agentB","entry","request","requestPath","pathProgress","activeAgents","filter","iterations","progress","step","nodesSearched","estimatedProgress","PlanState","Completed","getResult","Failed","Cancelled","waypoint","waypointThreshold","targetX","targetY","isLastWaypoint","newVelocity","allObstacles","validateVelocity","smoothSteering","applySmoothSteering","newPosition","resolveCollision","checkArrival","targetVelocity","maxChange","acceleration","dvx","dvy","changeMag","factor","newVel","targetSpeed","newSpeed","scale","corrections","activeEntities","i","j","entityA","entityB","collision","detectAgentCollision","collided","halfPush","penetration","corrA","normal","corrB","correction","updateOrder","Component","ECSComponent","Serializable","Serialize","Property","ORCAConfigComponent","Component","neighborDist","maxNeighbors","timeHorizon","timeHorizonObst","type","label","min","max","version","typeId"]}
|