@esengine/pathfinding 13.3.1 → 13.3.2

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/ecs.d.ts CHANGED
@@ -609,6 +609,31 @@ declare class NavigationSystem extends EntitySystem {
609
609
  * @en Resolve agent-agent collisions
610
610
  */
611
611
  private resolveAgentCollisions;
612
+ /**
613
+ * @zh 查找有效的修正向量(不会进入障碍物)
614
+ * @en Find valid correction vector (won't enter obstacles)
615
+ */
616
+ private findValidCorrection;
617
+ /**
618
+ * @zh 查找滑动位置(当目标位置不可行走时)
619
+ * @en Find slide position (when target position is not walkable)
620
+ *
621
+ * @zh 尝试只沿 X 轴或 Y 轴移动,实现沿墙滑动效果
622
+ * @en Try moving only along X or Y axis, achieving wall sliding effect
623
+ */
624
+ private findSlidePosition;
625
+ /**
626
+ * @zh 检查位置是否可行走(考虑代理半径)
627
+ * @en Check if position is walkable (considering agent radius)
628
+ *
629
+ * @zh 检查代理边界框的4个角点和中心点,确保整个代理都在可行走区域
630
+ * @en Check 4 corners and center of agent bounding box, ensure entire agent is in walkable area
631
+ *
632
+ * @param position - @zh 位置 @en Position
633
+ * @param radius - @zh 代理半径 @en Agent radius
634
+ * @param tolerance - @zh 容差比例 (0-1),用于允许轻微重叠 @en Tolerance ratio (0-1), allows slight overlap
635
+ */
636
+ private isPositionWalkable;
612
637
  }
613
638
 
614
639
  /**
package/dist/ecs.js CHANGED
@@ -1107,6 +1107,22 @@ var _NavigationSystem = class _NavigationSystem extends EntitySystem {
1107
1107
  if (this.config.enableCollisionResolution && this.collisionResolver && allObstacles.length > 0) {
1108
1108
  newPosition = this.collisionResolver.resolveCollision(newPosition, agent.radius, allObstacles);
1109
1109
  }
1110
+ if (!this.isPositionWalkable(newPosition, agent.radius)) {
1111
+ const slidPosition = this.findSlidePosition(agent.position, newPosition, agent.radius);
1112
+ if (slidPosition) {
1113
+ newPosition = slidPosition;
1114
+ agent.velocity = {
1115
+ x: (newPosition.x - agent.position.x) / deltaTime,
1116
+ y: (newPosition.y - agent.position.y) / deltaTime
1117
+ };
1118
+ } else {
1119
+ agent.velocity = {
1120
+ x: 0,
1121
+ y: 0
1122
+ };
1123
+ return;
1124
+ }
1125
+ }
1110
1126
  agent.position = newPosition;
1111
1127
  this.checkArrival(agent);
1112
1128
  }
@@ -1199,12 +1215,130 @@ var _NavigationSystem = class _NavigationSystem extends EntitySystem {
1199
1215
  x: agent.position.x + correction.x,
1200
1216
  y: agent.position.y + correction.y
1201
1217
  };
1218
+ if (!this.isPositionWalkable(newPosition, agent.radius, 1)) {
1219
+ const partialCorrection = this.findValidCorrection(agent.position, correction, agent.radius);
1220
+ if (partialCorrection) {
1221
+ newPosition = {
1222
+ x: agent.position.x + partialCorrection.x,
1223
+ y: agent.position.y + partialCorrection.y
1224
+ };
1225
+ } else {
1226
+ continue;
1227
+ }
1228
+ }
1202
1229
  if (allObstacles.length > 0) {
1203
1230
  newPosition = this.collisionResolver.resolveCollision(newPosition, agent.radius, allObstacles);
1204
1231
  }
1232
+ if (!this.isPositionWalkable(newPosition, agent.radius, 1)) {
1233
+ continue;
1234
+ }
1205
1235
  agent.position = newPosition;
1206
1236
  }
1207
1237
  }
1238
+ /**
1239
+ * @zh 查找有效的修正向量(不会进入障碍物)
1240
+ * @en Find valid correction vector (won't enter obstacles)
1241
+ */
1242
+ findValidCorrection(currentPos, correction, radius = 0) {
1243
+ if (!this.pathPlanner) return correction;
1244
+ const steps = [
1245
+ 0.75,
1246
+ 0.5,
1247
+ 0.25,
1248
+ 0.1
1249
+ ];
1250
+ for (const scale of steps) {
1251
+ const testPos = {
1252
+ x: currentPos.x + correction.x * scale,
1253
+ y: currentPos.y + correction.y * scale
1254
+ };
1255
+ if (this.isPositionWalkable(testPos, radius, 1)) {
1256
+ return {
1257
+ x: correction.x * scale,
1258
+ y: correction.y * scale
1259
+ };
1260
+ }
1261
+ }
1262
+ return null;
1263
+ }
1264
+ /**
1265
+ * @zh 查找滑动位置(当目标位置不可行走时)
1266
+ * @en Find slide position (when target position is not walkable)
1267
+ *
1268
+ * @zh 尝试只沿 X 轴或 Y 轴移动,实现沿墙滑动效果
1269
+ * @en Try moving only along X or Y axis, achieving wall sliding effect
1270
+ */
1271
+ findSlidePosition(currentPos, targetPos, radius) {
1272
+ const dx = targetPos.x - currentPos.x;
1273
+ const dy = targetPos.y - currentPos.y;
1274
+ const xOnlyPos = {
1275
+ x: targetPos.x,
1276
+ y: currentPos.y
1277
+ };
1278
+ if (Math.abs(dx) > 1e-3 && this.isPositionWalkable(xOnlyPos, radius)) {
1279
+ return xOnlyPos;
1280
+ }
1281
+ const yOnlyPos = {
1282
+ x: currentPos.x,
1283
+ y: targetPos.y
1284
+ };
1285
+ if (Math.abs(dy) > 1e-3 && this.isPositionWalkable(yOnlyPos, radius)) {
1286
+ return yOnlyPos;
1287
+ }
1288
+ const halfPos = {
1289
+ x: currentPos.x + dx * 0.5,
1290
+ y: currentPos.y + dy * 0.5
1291
+ };
1292
+ if (this.isPositionWalkable(halfPos, radius)) {
1293
+ return halfPos;
1294
+ }
1295
+ return null;
1296
+ }
1297
+ /**
1298
+ * @zh 检查位置是否可行走(考虑代理半径)
1299
+ * @en Check if position is walkable (considering agent radius)
1300
+ *
1301
+ * @zh 检查代理边界框的4个角点和中心点,确保整个代理都在可行走区域
1302
+ * @en Check 4 corners and center of agent bounding box, ensure entire agent is in walkable area
1303
+ *
1304
+ * @param position - @zh 位置 @en Position
1305
+ * @param radius - @zh 代理半径 @en Agent radius
1306
+ * @param tolerance - @zh 容差比例 (0-1),用于允许轻微重叠 @en Tolerance ratio (0-1), allows slight overlap
1307
+ */
1308
+ isPositionWalkable(position, radius = 0, tolerance = 0.8) {
1309
+ if (!this.pathPlanner) return true;
1310
+ if (!this.pathPlanner.isWalkable(position)) {
1311
+ return false;
1312
+ }
1313
+ if (radius <= 0) {
1314
+ return true;
1315
+ }
1316
+ const checkRadius = radius * tolerance;
1317
+ const corners = [
1318
+ {
1319
+ x: position.x - checkRadius,
1320
+ y: position.y - checkRadius
1321
+ },
1322
+ {
1323
+ x: position.x + checkRadius,
1324
+ y: position.y - checkRadius
1325
+ },
1326
+ {
1327
+ x: position.x - checkRadius,
1328
+ y: position.y + checkRadius
1329
+ },
1330
+ {
1331
+ x: position.x + checkRadius,
1332
+ y: position.y + checkRadius
1333
+ }
1334
+ ];
1335
+ for (const corner of corners) {
1336
+ if (!this.pathPlanner.isWalkable(corner)) {
1337
+ return false;
1338
+ }
1339
+ }
1340
+ return true;
1341
+ }
1208
1342
  };
1209
1343
  __name(_NavigationSystem, "NavigationSystem");
1210
1344
  var NavigationSystem = _NavigationSystem;
package/dist/ecs.js.map CHANGED
@@ -1 +1 @@
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 // 如果代理处于无法到达或已到达状态,停止移动\n // If agent is unreachable or arrived, stop moving\n if (agent.state === NavigationState.Unreachable || agent.state === NavigationState.Arrived) {\n return { x: 0, y: 0 };\n }\n\n // 如果没有路径且不在计算中,停止移动(避免直线穿墙)\n // If no path and not computing, stop moving (avoid walking through walls)\n if (agent.path.length === 0 && !agent.isComputingPath) {\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 if (agent.path.length > 0) {\n // 路径已走完,使用最终目标\n // Path exhausted, use final destination\n targetX = agent.destination.x;\n targetY = agent.destination.y;\n isLastWaypoint = true;\n } else {\n // 没有路径,不应该移动\n // No path, should not move\n return { x: 0, y: 0 };\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"]}
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 // 如果代理处于无法到达或已到达状态,停止移动\n // If agent is unreachable or arrived, stop moving\n if (agent.state === NavigationState.Unreachable || agent.state === NavigationState.Arrived) {\n return { x: 0, y: 0 };\n }\n\n // 如果没有路径且不在计算中,停止移动(避免直线穿墙)\n // If no path and not computing, stop moving (avoid walking through walls)\n if (agent.path.length === 0 && !agent.isComputingPath) {\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 if (agent.path.length > 0) {\n // 路径已走完,使用最终目标\n // Path exhausted, use final destination\n targetX = agent.destination.x;\n targetY = agent.destination.y;\n isLastWaypoint = true;\n } else {\n // 没有路径,不应该移动\n // No path, should not move\n return { x: 0, y: 0 };\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 // 最终检查:确保新位置可行走(防止出界或进入障碍物)\n // Final check: ensure new position is walkable (prevent out-of-bounds or entering obstacles)\n // 使用 isPositionWalkable 检查整个代理(包括半径)是否在可行走区域\n // Use isPositionWalkable to check if entire agent (including radius) is in walkable area\n if (!this.isPositionWalkable(newPosition, agent.radius)) {\n // 新位置不可行走,尝试滑动到有效位置\n // New position not walkable, try sliding to a valid position\n const slidPosition = this.findSlidePosition(agent.position, newPosition, agent.radius);\n if (slidPosition) {\n newPosition = slidPosition;\n // 更新速度以反映实际移动方向\n // Update velocity to reflect actual movement direction\n agent.velocity = {\n x: (newPosition.x - agent.position.x) / deltaTime,\n y: (newPosition.y - agent.position.y) / deltaTime\n };\n } else {\n // 无法滑动,保持原位置\n // Cannot slide, stay at current position\n agent.velocity = { x: 0, y: 0 };\n return;\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 // 先检查目标位置是否可行走(包括代理半径),防止被推入障碍物\n // Check if target position is walkable (including agent radius) first, prevent being pushed into obstacles\n // 使用严格的容差(1.0)防止代理被推入障碍物\n // Use strict tolerance (1.0) to prevent agents being pushed into obstacles\n if (!this.isPositionWalkable(newPosition, agent.radius, 1.0)) {\n // 目标位置不可行走,尝试只应用部分修正或完全不应用\n // Target position not walkable, try partial correction or skip entirely\n const partialCorrection = this.findValidCorrection(agent.position, correction, agent.radius);\n if (partialCorrection) {\n newPosition = {\n x: agent.position.x + partialCorrection.x,\n y: agent.position.y + partialCorrection.y\n };\n } else {\n // 找不到有效的修正方向,跳过此代理的修正\n // Cannot find valid correction direction, skip this agent\n continue;\n }\n }\n\n if (allObstacles.length > 0) {\n newPosition = this.collisionResolver.resolveCollision(\n newPosition,\n agent.radius,\n allObstacles\n );\n }\n\n // 最终检查:确保修正后的位置仍然可行走(包括代理半径)\n // Final check: ensure corrected position is still walkable (including agent radius)\n // 使用严格的容差(1.0)\n // Use strict tolerance (1.0)\n if (!this.isPositionWalkable(newPosition, agent.radius, 1.0)) {\n // 仍然不可行走,放弃此修正\n // Still not walkable, abandon this correction\n continue;\n }\n\n agent.position = newPosition;\n }\n }\n\n /**\n * @zh 查找有效的修正向量(不会进入障碍物)\n * @en Find valid correction vector (won't enter obstacles)\n */\n private findValidCorrection(currentPos: IVector2, correction: IVector2, radius: number = 0): IVector2 | null {\n if (!this.pathPlanner) return correction;\n\n // 尝试逐步减少修正量,找到一个有效的位置\n // Try progressively reducing correction to find a valid position\n // 使用严格的容差(1.0)确保不进入障碍物\n // Use strict tolerance (1.0) to ensure not entering obstacles\n const steps = [0.75, 0.5, 0.25, 0.1];\n for (const scale of steps) {\n const testPos: IVector2 = {\n x: currentPos.x + correction.x * scale,\n y: currentPos.y + correction.y * scale\n };\n if (this.isPositionWalkable(testPos, radius, 1.0)) {\n return { x: correction.x * scale, y: correction.y * scale };\n }\n }\n\n // 所有缩放都失败,返回 null\n // All scales failed, return null\n return null;\n }\n\n /**\n * @zh 查找滑动位置(当目标位置不可行走时)\n * @en Find slide position (when target position is not walkable)\n *\n * @zh 尝试只沿 X 轴或 Y 轴移动,实现沿墙滑动效果\n * @en Try moving only along X or Y axis, achieving wall sliding effect\n */\n private findSlidePosition(currentPos: IVector2, targetPos: IVector2, radius: number): IVector2 | null {\n const dx = targetPos.x - currentPos.x;\n const dy = targetPos.y - currentPos.y;\n\n // 尝试只沿 X 轴移动\n // Try moving only along X axis\n const xOnlyPos: IVector2 = { x: targetPos.x, y: currentPos.y };\n if (Math.abs(dx) > 0.001 && this.isPositionWalkable(xOnlyPos, radius)) {\n return xOnlyPos;\n }\n\n // 尝试只沿 Y 轴移动\n // Try moving only along Y axis\n const yOnlyPos: IVector2 = { x: currentPos.x, y: targetPos.y };\n if (Math.abs(dy) > 0.001 && this.isPositionWalkable(yOnlyPos, radius)) {\n return yOnlyPos;\n }\n\n // 尝试部分移动(50%)\n // Try partial movement (50%)\n const halfPos: IVector2 = {\n x: currentPos.x + dx * 0.5,\n y: currentPos.y + dy * 0.5\n };\n if (this.isPositionWalkable(halfPos, radius)) {\n return halfPos;\n }\n\n // 无法找到有效滑动位置\n // Cannot find valid slide position\n return null;\n }\n\n /**\n * @zh 检查位置是否可行走(考虑代理半径)\n * @en Check if position is walkable (considering agent radius)\n *\n * @zh 检查代理边界框的4个角点和中心点,确保整个代理都在可行走区域\n * @en Check 4 corners and center of agent bounding box, ensure entire agent is in walkable area\n *\n * @param position - @zh 位置 @en Position\n * @param radius - @zh 代理半径 @en Agent radius\n * @param tolerance - @zh 容差比例 (0-1),用于允许轻微重叠 @en Tolerance ratio (0-1), allows slight overlap\n */\n private isPositionWalkable(position: IVector2, radius: number = 0, tolerance: number = 0.8): boolean {\n if (!this.pathPlanner) return true;\n\n // 检查中心点(必须可行走)\n // Check center point (must be walkable)\n if (!this.pathPlanner.isWalkable(position)) {\n return false;\n }\n\n // 如果没有半径,只检查中心点\n // If no radius, only check center\n if (radius <= 0) {\n return true;\n }\n\n // 使用缩小后的半径进行检查,允许轻微重叠\n // Use reduced radius for checking, allows slight overlap\n const checkRadius = radius * tolerance;\n\n // 检查边界框的4个角点\n // Check 4 corners of bounding box\n const corners: IVector2[] = [\n { x: position.x - checkRadius, y: position.y - checkRadius },\n { x: position.x + checkRadius, y: position.y - checkRadius },\n { x: position.x - checkRadius, y: position.y + checkRadius },\n { x: position.x + checkRadius, y: position.y + checkRadius }\n ];\n\n for (const corner of corners) {\n if (!this.pathPlanner.isWalkable(corner)) {\n return false;\n }\n }\n\n return true;\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","isPositionWalkable","slidPosition","findSlidePosition","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","partialCorrection","findValidCorrection","currentPos","steps","testPos","targetPos","xOnlyPos","abs","yOnlyPos","halfPos","tolerance","isWalkable","checkRadius","corners","corner","updateOrder","Component","ECSComponent","Serializable","Serialize","Property","ORCAConfigComponent","Component","neighborDist","maxNeighbors","timeHorizon","timeHorizonObst","type","label","min","max","version","typeId"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esengine/pathfinding",
3
- "version": "13.3.1",
3
+ "version": "13.3.2",
4
4
  "description": "寻路系统 | Pathfinding System - A*, Grid, NavMesh",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -32,14 +32,14 @@
32
32
  "tsup": "^8.0.0",
33
33
  "typescript": "^5.8.0",
34
34
  "vitest": "^2.1.9",
35
+ "@esengine/ecs-framework-math": "2.11.0",
35
36
  "@esengine/blueprint": "4.5.0",
36
- "@esengine/ecs-framework": "2.7.1",
37
- "@esengine/ecs-framework-math": "2.11.0"
37
+ "@esengine/ecs-framework": "2.7.1"
38
38
  },
39
39
  "peerDependencies": {
40
- "@esengine/ecs-framework-math": "2.11.0",
41
40
  "@esengine/ecs-framework": "2.7.1",
42
- "@esengine/blueprint": "4.5.0"
41
+ "@esengine/blueprint": "4.5.0",
42
+ "@esengine/ecs-framework-math": "2.11.0"
43
43
  },
44
44
  "peerDependenciesMeta": {
45
45
  "@esengine/ecs-framework": {