@series-inc/rundot-3d-engine 0.6.7 → 0.6.8
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/README.md +80 -80
- package/dist/{chunk-SCNHMGS3.js → chunk-UDJVZHS6.js} +23 -1
- package/dist/{chunk-SCNHMGS3.js.map → chunk-UDJVZHS6.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/systems/index.d.ts +5 -0
- package/dist/systems/index.js +1 -1
- package/docs/systems/NavigationSystem.md +274 -274
- package/docs/systems/SplineSystem.md +270 -270
- package/package.json +95 -95
package/dist/index.js
CHANGED
package/dist/systems/index.d.ts
CHANGED
|
@@ -3334,6 +3334,11 @@ declare class StowKitSystem {
|
|
|
3334
3334
|
* Pre-decode skinned meshes so they're ready before the loading screen hides.
|
|
3335
3335
|
*/
|
|
3336
3336
|
preloadSkinnedMeshes(meshNames: string[], scale?: number): Promise<Map<string, THREE.Group>>;
|
|
3337
|
+
/**
|
|
3338
|
+
* Pre-decode audio clips so they're ready before the loading screen hides.
|
|
3339
|
+
* Decoded audio is cached in IndexedDB — subsequent loads are near-instant.
|
|
3340
|
+
*/
|
|
3341
|
+
preloadAudio(audioNames: string[]): Promise<Map<string, THREE.Audio>>;
|
|
3337
3342
|
/**
|
|
3338
3343
|
* Get a loaded pack by alias.
|
|
3339
3344
|
*/
|
package/dist/systems/index.js
CHANGED
|
@@ -1,274 +1,274 @@
|
|
|
1
|
-
# Navigation System
|
|
2
|
-
|
|
3
|
-
Grid-based 2D navigation with A* pathfinding, obstacle management, and AI agent movement.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { DynamicNavSystem, NavAgent } from "@series-inc/rundot-3d-engine/systems"
|
|
9
|
-
import * as THREE from "three"
|
|
10
|
-
|
|
11
|
-
// Initialize the navigation system
|
|
12
|
-
DynamicNavSystem.initialize(
|
|
13
|
-
scene, // THREE.Scene (optional, for debug visualization)
|
|
14
|
-
200, // World width
|
|
15
|
-
200, // World depth
|
|
16
|
-
2, // Grid cell size
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
// Add obstacles
|
|
20
|
-
DynamicNavSystem.addBoxObstacle(10, 10, 4, 4)
|
|
21
|
-
|
|
22
|
-
// Check walkability
|
|
23
|
-
const canWalk = DynamicNavSystem.isWalkable(5, 5)
|
|
24
|
-
|
|
25
|
-
// Find a path
|
|
26
|
-
const result = DynamicNavSystem.findPath(
|
|
27
|
-
new THREE.Vector2(0, 0),
|
|
28
|
-
new THREE.Vector2(20, 20),
|
|
29
|
-
)
|
|
30
|
-
if (result.success) {
|
|
31
|
-
console.log(`Path found: ${result.waypoints.length} waypoints, distance: ${result.distance}`)
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## AI Agent Movement
|
|
36
|
-
|
|
37
|
-
The `NavAgent` component provides automatic pathfinding and movement for GameObjects.
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { NavAgent } from "@series-inc/rundot-3d-engine/systems"
|
|
41
|
-
|
|
42
|
-
class Enemy extends Component {
|
|
43
|
-
private navAgent?: NavAgent
|
|
44
|
-
|
|
45
|
-
protected onCreate(): void {
|
|
46
|
-
this.navAgent = new NavAgent()
|
|
47
|
-
this.navAgent.moveSpeed = 3.0
|
|
48
|
-
this.navAgent.arrivalDistance = 0.5
|
|
49
|
-
this.gameObject.addComponent(this.navAgent)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
public update(deltaTime: number): void {
|
|
53
|
-
if (this.navAgent?.hasReachedTarget()) {
|
|
54
|
-
this.navAgent.moveTo(this.getNextPatrolPoint())
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Use normalized speed for animation blending
|
|
58
|
-
const speedNorm = this.navAgent?.getMovementSpeedNormalized() ?? 0
|
|
59
|
-
this.animator?.setBlendWeight("walk", speedNorm)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### NavAgent Properties
|
|
65
|
-
|
|
66
|
-
- `moveSpeed: number` — movement speed (default: 5.0)
|
|
67
|
-
- `acceleration: number` — movement acceleration (default: 15.0)
|
|
68
|
-
- `deceleration: number` — movement deceleration (default: 10.0)
|
|
69
|
-
- `arrivalDistance: number` — distance threshold for waypoint arrival (default: 0.5)
|
|
70
|
-
- `angularAcceleration: number` — rotation speed (default: 8.0)
|
|
71
|
-
|
|
72
|
-
### NavAgent Methods
|
|
73
|
-
|
|
74
|
-
- `moveTo(target: THREE.Vector3 | THREE.Vector2): boolean` — move to target using pathfinding (returns `true` if path found)
|
|
75
|
-
- `stop(): void` — stop current movement
|
|
76
|
-
- `hasReachedTarget(): boolean` — check if agent has reached its target
|
|
77
|
-
- `isInMotion(): boolean` — check if agent is currently moving
|
|
78
|
-
- `getMovementSpeedNormalized(): number` — get speed as 0–1 (for animation blending)
|
|
79
|
-
- `getMovementSpeed(): number` — get current speed in units/second
|
|
80
|
-
- `getCurrentSpeed(): number` — alias for `getMovementSpeed()`
|
|
81
|
-
- `getWaypoints(): THREE.Vector3[]` — get current waypoints (copy)
|
|
82
|
-
- `setVisualizationEnabled(enabled: boolean): void` — enable/disable path visualization
|
|
83
|
-
|
|
84
|
-
## Obstacle Management
|
|
85
|
-
|
|
86
|
-
### Box Obstacles
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
// Add a box obstacle (center position + size)
|
|
90
|
-
DynamicNavSystem.addBoxObstacle(x, z, width, depth)
|
|
91
|
-
|
|
92
|
-
// Remove a box obstacle
|
|
93
|
-
DynamicNavSystem.removeBoxObstacle(x, z, width, depth)
|
|
94
|
-
|
|
95
|
-
// Add from a GameObject with RigidBody
|
|
96
|
-
DynamicNavSystem.addBoxObstacleFromRigidBody(gameObject)
|
|
97
|
-
|
|
98
|
-
// Add from bounds
|
|
99
|
-
DynamicNavSystem.addBoxObstacleFromBounds(gameObject, boundsSize)
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Rotated Obstacles
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
// Add a rotated box obstacle (returns ID for later removal)
|
|
106
|
-
const id = DynamicNavSystem.addRotatedBoxObstacle(gameObject, boundsSize)
|
|
107
|
-
|
|
108
|
-
// Remove by ID
|
|
109
|
-
DynamicNavSystem.removeObstacleById(id)
|
|
110
|
-
|
|
111
|
-
// Remove by GameObject
|
|
112
|
-
DynamicNavSystem.removeObstacleByGameObject(gameObject)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Raw Footprint API
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
import { NavigationGrid, Footprint } from "@series-inc/rundot-3d-engine/systems"
|
|
119
|
-
|
|
120
|
-
// Polygon footprint
|
|
121
|
-
const footprint: Footprint = {
|
|
122
|
-
type: "polygon",
|
|
123
|
-
vertices: [new THREE.Vector3(0, 0, 0), new THREE.Vector3(4, 0, 0), new THREE.Vector3(4, 0, 4)],
|
|
124
|
-
}
|
|
125
|
-
DynamicNavSystem.addObstacle(footprint)
|
|
126
|
-
|
|
127
|
-
// Circle footprint
|
|
128
|
-
const circle: Footprint = {
|
|
129
|
-
type: "circle",
|
|
130
|
-
x: 10,
|
|
131
|
-
z: 10,
|
|
132
|
-
radius: 3,
|
|
133
|
-
}
|
|
134
|
-
DynamicNavSystem.addObstacle(circle)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Pathfinding
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
// Find a path (accepts Vector2 or Vector3)
|
|
141
|
-
const result = DynamicNavSystem.findPath(startPos, endPos)
|
|
142
|
-
|
|
143
|
-
// Result:
|
|
144
|
-
// {
|
|
145
|
-
// success: boolean,
|
|
146
|
-
// waypoints: Waypoint[], // { x, z } objects
|
|
147
|
-
// distance: number,
|
|
148
|
-
// }
|
|
149
|
-
|
|
150
|
-
// Quick reachability check (faster than full pathfinding)
|
|
151
|
-
const reachable = DynamicNavSystem.canReach(startX, startZ, endX, endZ)
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Debug Visualization
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// Print grid state to console
|
|
158
|
-
DynamicNavSystem.debugNavigation()
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Path Visualization
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
import { PathVisualizationThree } from "@series-inc/rundot-3d-engine/systems"
|
|
165
|
-
|
|
166
|
-
// Initialize
|
|
167
|
-
PathVisualizationThree.initialize(scene)
|
|
168
|
-
|
|
169
|
-
// Visualize a path
|
|
170
|
-
PathVisualizationThree.addPath("myPath", pathResult)
|
|
171
|
-
|
|
172
|
-
// Remove a specific path
|
|
173
|
-
PathVisualizationThree.removePath("myPath")
|
|
174
|
-
|
|
175
|
-
// Toggle all visualization
|
|
176
|
-
PathVisualizationThree.setVisualizationEnabled(true)
|
|
177
|
-
|
|
178
|
-
// Query
|
|
179
|
-
PathVisualizationThree.getActivePathIds()
|
|
180
|
-
PathVisualizationThree.hasActiveVisualization()
|
|
181
|
-
|
|
182
|
-
// Clear all
|
|
183
|
-
PathVisualizationThree.clearVisualization()
|
|
184
|
-
PathVisualizationThree.dispose()
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## API Reference
|
|
188
|
-
|
|
189
|
-
### DynamicNavSystem (Static Class)
|
|
190
|
-
|
|
191
|
-
#### Initialization
|
|
192
|
-
|
|
193
|
-
- `initialize(scene?, worldWidth?, worldDepth?, gridSize?): void` — initialize the system
|
|
194
|
-
- `getIsInitialized(): boolean` — check if initialized
|
|
195
|
-
- `dispose(): void` — clean up
|
|
196
|
-
|
|
197
|
-
#### Obstacle Management
|
|
198
|
-
|
|
199
|
-
- `addObstacle(footprint: Footprint): void` — add a raw footprint obstacle
|
|
200
|
-
- `removeObstacle(footprint: Footprint): void` — remove a raw footprint obstacle
|
|
201
|
-
- `addBoxObstacle(x, z, width, depth): void` — add a box obstacle
|
|
202
|
-
- `removeBoxObstacle(x, z, width, depth): void` — remove a box obstacle
|
|
203
|
-
- `addBoxObstacleFromRigidBody(gameObject): void` — add obstacle from RigidBody
|
|
204
|
-
- `addBoxObstacleFromBounds(gameObject, boundsSize): void` — add obstacle from bounds
|
|
205
|
-
- `addRotatedBoxObstacle(gameObject, boundsSize): string` — add rotated obstacle (returns ID)
|
|
206
|
-
- `removeObstacleById(id): boolean` — remove by ID
|
|
207
|
-
- `removeObstacleByGameObject(gameObject): boolean` — remove by GameObject
|
|
208
|
-
|
|
209
|
-
#### Queries
|
|
210
|
-
|
|
211
|
-
- `isWalkable(x, z): boolean` — check if a position is walkable
|
|
212
|
-
- `worldToGrid(x, z): { col, row } | null` — convert world to grid coords
|
|
213
|
-
- `gridToWorld(col, row): { x, z } | null` — convert grid to world coords
|
|
214
|
-
- `findPath(startPos, endPos): PathfindingResult` — A* pathfinding
|
|
215
|
-
- `canReach(startX, startZ, endX, endZ): boolean` — quick reachability check
|
|
216
|
-
|
|
217
|
-
#### Debug
|
|
218
|
-
|
|
219
|
-
- `debugNavigation(): void` — print navigation state
|
|
220
|
-
|
|
221
|
-
### NavigationGrid
|
|
222
|
-
|
|
223
|
-
- `constructor(worldWidth, worldDepth, gridSize)` — create a grid
|
|
224
|
-
- `worldToGrid(x, z): { col, row }` — convert coordinates
|
|
225
|
-
- `gridToWorld(col, row): { x, z }` — convert coordinates
|
|
226
|
-
- `addObstacle(footprint): void` — add obstacle with reference counting
|
|
227
|
-
- `removeObstacle(footprint): void` — remove obstacle
|
|
228
|
-
- `isWalkable(col, row): boolean` — check cell walkability
|
|
229
|
-
- `getDimensions(): { cols, rows, worldWidth, worldDepth, gridSize }` — get grid info
|
|
230
|
-
- `getGridData(): number[][]` — get raw grid data
|
|
231
|
-
- `printGrid(): void` — debug print
|
|
232
|
-
- `static setDebugMode(enabled): void` — toggle debug logging
|
|
233
|
-
- `static isDebugMode(): boolean` — check debug mode
|
|
234
|
-
|
|
235
|
-
### Interfaces
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
interface Footprint {
|
|
239
|
-
type: "polygon" | "circle"
|
|
240
|
-
vertices?: Vector3[] // For polygon
|
|
241
|
-
x?: number // For circle
|
|
242
|
-
z?: number // For circle
|
|
243
|
-
radius?: number // For circle
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
interface Waypoint {
|
|
247
|
-
x: number
|
|
248
|
-
z: number
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
interface PathfindingResult {
|
|
252
|
-
success: boolean
|
|
253
|
-
waypoints: Waypoint[]
|
|
254
|
-
distance: number
|
|
255
|
-
}
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Performance
|
|
259
|
-
|
|
260
|
-
- **Obstacle add/remove:** O(footprint_area / cell_size²)
|
|
261
|
-
- **Walkability check:** O(1)
|
|
262
|
-
- **Pathfinding:** A* with 8-directional movement, line-of-sight optimization
|
|
263
|
-
|
|
264
|
-
## Configuration
|
|
265
|
-
|
|
266
|
-
- **Smaller cells (0.5–1.0):** Higher precision, more memory
|
|
267
|
-
- **Medium cells (1.0–2.0):** Good balance for most games
|
|
268
|
-
- **Larger cells (2.0–5.0):** Lower precision, less memory, faster updates
|
|
269
|
-
|
|
270
|
-
## Related Systems
|
|
271
|
-
|
|
272
|
-
- [PhysicsSystem](../physics/PhysicsSystem.md) - RigidBody-based obstacle detection
|
|
273
|
-
- [Component](../core/Component.md) - NavAgent is a component
|
|
274
|
-
- [GameObject](../core/GameObject.md) - Obstacle and agent GameObjects
|
|
1
|
+
# Navigation System
|
|
2
|
+
|
|
3
|
+
Grid-based 2D navigation with A* pathfinding, obstacle management, and AI agent movement.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { DynamicNavSystem, NavAgent } from "@series-inc/rundot-3d-engine/systems"
|
|
9
|
+
import * as THREE from "three"
|
|
10
|
+
|
|
11
|
+
// Initialize the navigation system
|
|
12
|
+
DynamicNavSystem.initialize(
|
|
13
|
+
scene, // THREE.Scene (optional, for debug visualization)
|
|
14
|
+
200, // World width
|
|
15
|
+
200, // World depth
|
|
16
|
+
2, // Grid cell size
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
// Add obstacles
|
|
20
|
+
DynamicNavSystem.addBoxObstacle(10, 10, 4, 4)
|
|
21
|
+
|
|
22
|
+
// Check walkability
|
|
23
|
+
const canWalk = DynamicNavSystem.isWalkable(5, 5)
|
|
24
|
+
|
|
25
|
+
// Find a path
|
|
26
|
+
const result = DynamicNavSystem.findPath(
|
|
27
|
+
new THREE.Vector2(0, 0),
|
|
28
|
+
new THREE.Vector2(20, 20),
|
|
29
|
+
)
|
|
30
|
+
if (result.success) {
|
|
31
|
+
console.log(`Path found: ${result.waypoints.length} waypoints, distance: ${result.distance}`)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## AI Agent Movement
|
|
36
|
+
|
|
37
|
+
The `NavAgent` component provides automatic pathfinding and movement for GameObjects.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { NavAgent } from "@series-inc/rundot-3d-engine/systems"
|
|
41
|
+
|
|
42
|
+
class Enemy extends Component {
|
|
43
|
+
private navAgent?: NavAgent
|
|
44
|
+
|
|
45
|
+
protected onCreate(): void {
|
|
46
|
+
this.navAgent = new NavAgent()
|
|
47
|
+
this.navAgent.moveSpeed = 3.0
|
|
48
|
+
this.navAgent.arrivalDistance = 0.5
|
|
49
|
+
this.gameObject.addComponent(this.navAgent)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public update(deltaTime: number): void {
|
|
53
|
+
if (this.navAgent?.hasReachedTarget()) {
|
|
54
|
+
this.navAgent.moveTo(this.getNextPatrolPoint())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Use normalized speed for animation blending
|
|
58
|
+
const speedNorm = this.navAgent?.getMovementSpeedNormalized() ?? 0
|
|
59
|
+
this.animator?.setBlendWeight("walk", speedNorm)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### NavAgent Properties
|
|
65
|
+
|
|
66
|
+
- `moveSpeed: number` — movement speed (default: 5.0)
|
|
67
|
+
- `acceleration: number` — movement acceleration (default: 15.0)
|
|
68
|
+
- `deceleration: number` — movement deceleration (default: 10.0)
|
|
69
|
+
- `arrivalDistance: number` — distance threshold for waypoint arrival (default: 0.5)
|
|
70
|
+
- `angularAcceleration: number` — rotation speed (default: 8.0)
|
|
71
|
+
|
|
72
|
+
### NavAgent Methods
|
|
73
|
+
|
|
74
|
+
- `moveTo(target: THREE.Vector3 | THREE.Vector2): boolean` — move to target using pathfinding (returns `true` if path found)
|
|
75
|
+
- `stop(): void` — stop current movement
|
|
76
|
+
- `hasReachedTarget(): boolean` — check if agent has reached its target
|
|
77
|
+
- `isInMotion(): boolean` — check if agent is currently moving
|
|
78
|
+
- `getMovementSpeedNormalized(): number` — get speed as 0–1 (for animation blending)
|
|
79
|
+
- `getMovementSpeed(): number` — get current speed in units/second
|
|
80
|
+
- `getCurrentSpeed(): number` — alias for `getMovementSpeed()`
|
|
81
|
+
- `getWaypoints(): THREE.Vector3[]` — get current waypoints (copy)
|
|
82
|
+
- `setVisualizationEnabled(enabled: boolean): void` — enable/disable path visualization
|
|
83
|
+
|
|
84
|
+
## Obstacle Management
|
|
85
|
+
|
|
86
|
+
### Box Obstacles
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Add a box obstacle (center position + size)
|
|
90
|
+
DynamicNavSystem.addBoxObstacle(x, z, width, depth)
|
|
91
|
+
|
|
92
|
+
// Remove a box obstacle
|
|
93
|
+
DynamicNavSystem.removeBoxObstacle(x, z, width, depth)
|
|
94
|
+
|
|
95
|
+
// Add from a GameObject with RigidBody
|
|
96
|
+
DynamicNavSystem.addBoxObstacleFromRigidBody(gameObject)
|
|
97
|
+
|
|
98
|
+
// Add from bounds
|
|
99
|
+
DynamicNavSystem.addBoxObstacleFromBounds(gameObject, boundsSize)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Rotated Obstacles
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Add a rotated box obstacle (returns ID for later removal)
|
|
106
|
+
const id = DynamicNavSystem.addRotatedBoxObstacle(gameObject, boundsSize)
|
|
107
|
+
|
|
108
|
+
// Remove by ID
|
|
109
|
+
DynamicNavSystem.removeObstacleById(id)
|
|
110
|
+
|
|
111
|
+
// Remove by GameObject
|
|
112
|
+
DynamicNavSystem.removeObstacleByGameObject(gameObject)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Raw Footprint API
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { NavigationGrid, Footprint } from "@series-inc/rundot-3d-engine/systems"
|
|
119
|
+
|
|
120
|
+
// Polygon footprint
|
|
121
|
+
const footprint: Footprint = {
|
|
122
|
+
type: "polygon",
|
|
123
|
+
vertices: [new THREE.Vector3(0, 0, 0), new THREE.Vector3(4, 0, 0), new THREE.Vector3(4, 0, 4)],
|
|
124
|
+
}
|
|
125
|
+
DynamicNavSystem.addObstacle(footprint)
|
|
126
|
+
|
|
127
|
+
// Circle footprint
|
|
128
|
+
const circle: Footprint = {
|
|
129
|
+
type: "circle",
|
|
130
|
+
x: 10,
|
|
131
|
+
z: 10,
|
|
132
|
+
radius: 3,
|
|
133
|
+
}
|
|
134
|
+
DynamicNavSystem.addObstacle(circle)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Pathfinding
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Find a path (accepts Vector2 or Vector3)
|
|
141
|
+
const result = DynamicNavSystem.findPath(startPos, endPos)
|
|
142
|
+
|
|
143
|
+
// Result:
|
|
144
|
+
// {
|
|
145
|
+
// success: boolean,
|
|
146
|
+
// waypoints: Waypoint[], // { x, z } objects
|
|
147
|
+
// distance: number,
|
|
148
|
+
// }
|
|
149
|
+
|
|
150
|
+
// Quick reachability check (faster than full pathfinding)
|
|
151
|
+
const reachable = DynamicNavSystem.canReach(startX, startZ, endX, endZ)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Debug Visualization
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Print grid state to console
|
|
158
|
+
DynamicNavSystem.debugNavigation()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Path Visualization
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { PathVisualizationThree } from "@series-inc/rundot-3d-engine/systems"
|
|
165
|
+
|
|
166
|
+
// Initialize
|
|
167
|
+
PathVisualizationThree.initialize(scene)
|
|
168
|
+
|
|
169
|
+
// Visualize a path
|
|
170
|
+
PathVisualizationThree.addPath("myPath", pathResult)
|
|
171
|
+
|
|
172
|
+
// Remove a specific path
|
|
173
|
+
PathVisualizationThree.removePath("myPath")
|
|
174
|
+
|
|
175
|
+
// Toggle all visualization
|
|
176
|
+
PathVisualizationThree.setVisualizationEnabled(true)
|
|
177
|
+
|
|
178
|
+
// Query
|
|
179
|
+
PathVisualizationThree.getActivePathIds()
|
|
180
|
+
PathVisualizationThree.hasActiveVisualization()
|
|
181
|
+
|
|
182
|
+
// Clear all
|
|
183
|
+
PathVisualizationThree.clearVisualization()
|
|
184
|
+
PathVisualizationThree.dispose()
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## API Reference
|
|
188
|
+
|
|
189
|
+
### DynamicNavSystem (Static Class)
|
|
190
|
+
|
|
191
|
+
#### Initialization
|
|
192
|
+
|
|
193
|
+
- `initialize(scene?, worldWidth?, worldDepth?, gridSize?): void` — initialize the system
|
|
194
|
+
- `getIsInitialized(): boolean` — check if initialized
|
|
195
|
+
- `dispose(): void` — clean up
|
|
196
|
+
|
|
197
|
+
#### Obstacle Management
|
|
198
|
+
|
|
199
|
+
- `addObstacle(footprint: Footprint): void` — add a raw footprint obstacle
|
|
200
|
+
- `removeObstacle(footprint: Footprint): void` — remove a raw footprint obstacle
|
|
201
|
+
- `addBoxObstacle(x, z, width, depth): void` — add a box obstacle
|
|
202
|
+
- `removeBoxObstacle(x, z, width, depth): void` — remove a box obstacle
|
|
203
|
+
- `addBoxObstacleFromRigidBody(gameObject): void` — add obstacle from RigidBody
|
|
204
|
+
- `addBoxObstacleFromBounds(gameObject, boundsSize): void` — add obstacle from bounds
|
|
205
|
+
- `addRotatedBoxObstacle(gameObject, boundsSize): string` — add rotated obstacle (returns ID)
|
|
206
|
+
- `removeObstacleById(id): boolean` — remove by ID
|
|
207
|
+
- `removeObstacleByGameObject(gameObject): boolean` — remove by GameObject
|
|
208
|
+
|
|
209
|
+
#### Queries
|
|
210
|
+
|
|
211
|
+
- `isWalkable(x, z): boolean` — check if a position is walkable
|
|
212
|
+
- `worldToGrid(x, z): { col, row } | null` — convert world to grid coords
|
|
213
|
+
- `gridToWorld(col, row): { x, z } | null` — convert grid to world coords
|
|
214
|
+
- `findPath(startPos, endPos): PathfindingResult` — A* pathfinding
|
|
215
|
+
- `canReach(startX, startZ, endX, endZ): boolean` — quick reachability check
|
|
216
|
+
|
|
217
|
+
#### Debug
|
|
218
|
+
|
|
219
|
+
- `debugNavigation(): void` — print navigation state
|
|
220
|
+
|
|
221
|
+
### NavigationGrid
|
|
222
|
+
|
|
223
|
+
- `constructor(worldWidth, worldDepth, gridSize)` — create a grid
|
|
224
|
+
- `worldToGrid(x, z): { col, row }` — convert coordinates
|
|
225
|
+
- `gridToWorld(col, row): { x, z }` — convert coordinates
|
|
226
|
+
- `addObstacle(footprint): void` — add obstacle with reference counting
|
|
227
|
+
- `removeObstacle(footprint): void` — remove obstacle
|
|
228
|
+
- `isWalkable(col, row): boolean` — check cell walkability
|
|
229
|
+
- `getDimensions(): { cols, rows, worldWidth, worldDepth, gridSize }` — get grid info
|
|
230
|
+
- `getGridData(): number[][]` — get raw grid data
|
|
231
|
+
- `printGrid(): void` — debug print
|
|
232
|
+
- `static setDebugMode(enabled): void` — toggle debug logging
|
|
233
|
+
- `static isDebugMode(): boolean` — check debug mode
|
|
234
|
+
|
|
235
|
+
### Interfaces
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
interface Footprint {
|
|
239
|
+
type: "polygon" | "circle"
|
|
240
|
+
vertices?: Vector3[] // For polygon
|
|
241
|
+
x?: number // For circle
|
|
242
|
+
z?: number // For circle
|
|
243
|
+
radius?: number // For circle
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
interface Waypoint {
|
|
247
|
+
x: number
|
|
248
|
+
z: number
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
interface PathfindingResult {
|
|
252
|
+
success: boolean
|
|
253
|
+
waypoints: Waypoint[]
|
|
254
|
+
distance: number
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Performance
|
|
259
|
+
|
|
260
|
+
- **Obstacle add/remove:** O(footprint_area / cell_size²)
|
|
261
|
+
- **Walkability check:** O(1)
|
|
262
|
+
- **Pathfinding:** A* with 8-directional movement, line-of-sight optimization
|
|
263
|
+
|
|
264
|
+
## Configuration
|
|
265
|
+
|
|
266
|
+
- **Smaller cells (0.5–1.0):** Higher precision, more memory
|
|
267
|
+
- **Medium cells (1.0–2.0):** Good balance for most games
|
|
268
|
+
- **Larger cells (2.0–5.0):** Lower precision, less memory, faster updates
|
|
269
|
+
|
|
270
|
+
## Related Systems
|
|
271
|
+
|
|
272
|
+
- [PhysicsSystem](../physics/PhysicsSystem.md) - RigidBody-based obstacle detection
|
|
273
|
+
- [Component](../core/Component.md) - NavAgent is a component
|
|
274
|
+
- [GameObject](../core/GameObject.md) - Obstacle and agent GameObjects
|