Generating a NavMesh
The easiest way to generate a NavMesh is using the high level generator functions from recast-navigation/generators:
generateSoloNavMesh– Generates a NavMesh with a single tile. You can use this for smaller environments.generateTiledNavMesh– Generates a NavMesh with multiple tiles. You should use this for larger environments.generateTileCache– Generates a TileCache that supports temporary obstacles. See the Temporary Obstacles section.
The input positions and indices should adhere to OpenGL conventions:
- Use the right-handed coordinate system
- Indices should be in counter-clockwise winding order
- The
positionsandindicesarguments should be flat arrays of numbers
import { generateSoloNavMesh } from 'recast-navigation/generators';
const positions = [
/* flat array of positions */
/* e.g. x1, y1, z1, x2, y2, z2, ... */
];
const indices = [
/* flat array of indices */
];
const navMeshConfig = {
/* ... */
};
const { success, navMesh } = generateSoloNavMesh(
positions,
indices,
navMeshConfig
);
Builing a NavMesh in a Web Worker
It’s possible to build a NavMesh in a Web Worker. This can be useful for offloading heavy computation from the main thread.
The library doesn’t include a web worker, but it’s straightforward to create your own. An example of solo nav mesh generation in a web worker can be found here: https://github.com/isaac-mason/recast-navigation-js/tree/next/examples/three-vite-worker-example
The example uses importNavMesh and exportNavMesh to serialize and deserialize a NavMesh for transfer between the main thread and the web worker.
Customizing the NavMesh Generation Process
This library provides low-level APIs that aim to match the recast and detour c++ api, allowing you to create custom navigation mesh generators based on your specific needs. You can use the NavMesh generators provided by @recast-navigation/generators as a basis: https://github.com/isaac-mason/recast-navigation-js/tree/main/packages/recast-navigation-generators/src/generators
An example of a custom NavMesh generator with custom areas can be found here: https://recast-navigation-js.isaacmason.com/?path=/story/advanced-custom-areas–compute-path
Please note that not all recast and detour functionality is exposed yet. If you require unexposed functionality, please submit an issue or a pull request.
Querying a NavMesh
Creating a NavMeshQuery class
import { NavMeshQuery } from 'recast-navigation';
const navMeshQuery = new NavMeshQuery(navMesh);
Compute a straight path between two points
const start = { x: 0, y: 0, z: 0 };
const end = { x: 2, y: 0, z: 0 };
const { success, error, path } = navMeshQuery.computePath(start, end);
Find the closest point on the NavMesh to a given position
const position = { x: 0, y: 0, z: 0 };
const { success, status, point, polyRef, isPointOverPoly } =
navMeshQuery.findClosestPoint(position);
Find a random point on the NavMesh around a given position
const radius = 0.5;
const {
success,
status,
randomPolyRef,
randomPoint: initialAgentPosition,
} = navMeshQuery.findRandomPointAroundCircle(position, radius);
Crowds and Agents
Creating a Crowd
import { Crowd } from 'recast-navigation';
const maxAgents = 10;
const maxAgentRadius = 0.6;
const crowd = new Crowd(navMesh, { maxAgents, maxAgentRadius });
Updating a Crowd
There are a few options for updating a crowd:
Variable time stepping
The simplest approach is to do varible time stepping. Simply call crowd.update with your delta time every frame.
crowd.update(timeSinceLastFrame);
This approach is suitable for most use cases, but the variable timestep will result in non-deterministic behaviour.
Depending on your use case, you might want to clamp timeSinceLastFrame to a maximum value to prevent large time steps causing issues.
Fixed time stepping with interpolation
Fixed time stepping with interpolation can be preferable if you need deterministic behaviour, but still want smooth agent position updates each frame.
If you provide update with a dt value and a timeSinceLastFrame value, the crowd update will do fixed time stepping with interpolation.
const dt = 1 / 60; const maxSubSteps = 10; crowd.update(dt, timeSinceLastFrame, maxSubSteps);
This will update the interpolatedPosition vector3 on each agent, which you can use to render a smoothly interpolated agent position between updates.
console.log(agent.interpolatedPosition); // { x: 1, y: 2, z: 3 }
Manual fixed time stepping
If you want full control over crowd updates, you can simply call crowd.update with a given dt value.
const dt = 1 / 60; crowd.update(dt);
Creating an Agent in a Crowd
const position = { x: 0, y: 0, z: 0 };
const radius = 2;
const {
success,
status,
randomPolyRef,
randomPoint: initialAgentPosition,
} = navMeshQuery.findRandomPointAroundCircle(position, radius);
const agent = crowd.addAgent(initialAgentPosition, {
radius: 0.5,
height: 0.5,
maxAcceleration: 4.0,
maxSpeed: 1.0,
collisionQueryRange: 0.5,
pathOptimizationRange: 0.0,
separationWeight: 1.0,
});
Setting an Agent’s Target
const targetPosition = { x: 2, y: 0, z: 0 };
agent.requestMoveTarget(targetPosition);
Clearing an Agent’s Target
agent.resetMoveTarget();