NavCat:纯 JavaScript 的 3D 导航网格构建与查询库
阅读时间: 大约 10 分钟
NavCat:纯 JavaScript 的 3D 导航网格构建与查询库

NavCat 是一个面向 3D 地面导航的 JavaScript 导航网格(navigation mesh,navmesh)构建与查询库,官网 navcat.dev,核心定位是”在纯 JS 里完成 navmesh 生成与寻路查询”,且与渲染/游戏引擎解耦。它的体素化生成路线借鉴了经典的 recastnavigation 库,但官方明确说明不是 recast 的直接移植。本文基于官方 README 与文档,解析其管线、参数体系与边界。
一、背景:JS 3D 寻路的空白
在 2D 俯视角里,寻路用格子图或导航多边形就够;一旦进入 3D(Three.js/Babylon.js 做的室内导航、网页 3D 展厅、轻量游戏),角色要沿地面走、绕开墙体、爬得上台阶、上不去陡坡,就需要一张”可走表面图”。C# 生态有 Unity 的 NavMesh + recast 成熟方案,原生 C++ 有 recastnavigation,而浏览器侧长期缺少一个既能离线烘焙 navmesh、又能在运行时做 A* 查询的纯 JS 实现。NavCat 填的就是这个位置。
二、它是什么:生成 + 查询一体,引擎无关
官方 Features 列出的能力:
- 从 3D 几何构建导航网格;
- 导航网格查询(寻路等);
- 单块(single-tile)与多块(tiled)导航网格;
- 数据结构完全 JSON 可序列化;
- 纯 JS,面向 tree-shaking 设计;
- 与任何 JS 引擎配合:Babylon.js、PlayCanvas、Three.js,或自研引擎;
- 提供
navcat/three入口的 Three.js 专用工具。
底层结构上,navmesh 由一个或多个 tile 组成,每个 tile 内含可走多边形与高度细节;NavCat 在背后维护一张”节点(多边形)—连接(多边形邻接)“图,寻路查询就是在这张图上搜索。大多数项目用覆盖整关的单块即可,大世界/动态场景才需要分块。
三、八步生成管线(recast 体素化路线)
官方 Deep Dive 把生成拆成八步,核心是把三角形几何栅格化成体素/高度场,再过滤、分区、抽边、三角化:
- 标记可走三角形:按坡度等条件先在输入几何上筛出角色能站的面;
- 栅格化为高度场并过滤:把可走三角形 voxelize 进 heightfield,移除角色站不住的跨度与悬垂;
- 构建紧凑高度场、按角色半径腐蚀、标记区域:只保留顶层可走面,按 agent 半径内缩留出墙体间隙;
- 构建区域(regions):先算距离场,再用 watershed 或单调分区把可走面切成无洞区域;
- 由区域构建轮廓(contours);
- 由轮廓生成多边形网格,并构建细节高度网格;
- 把构建期 poly mesh 与 detail mesh 转成运行时 tile 格式;
- 组装最终导航网格。

运行时查询的最小调用形如:
import { DEFAULT_QUERY_FILTER, findPath } from 'navcat';
const path = findPath(navMesh, start, end, halfExtents, DEFAULT_QUERY_FILTER);返回结果除了路径点,还带 nodePath(A* 搜索节点),官方提供 createSearchNodesHelper 把搜索过程可视化。
四、关键参数:以 1 米人形角色为基准的启发式
官方参数表给出了 1=1m 世界、人形 agent 的起步区间:
| 参数 | 含义 | 官方建议区间(1m 人形) |
|---|---|---|
cellSize | 水平体素尺寸(XZ),越小越精细、生成越慢 | 约 walkableRadiusWorld / 3 |
cellHeight | 垂直体素尺寸(Y) | 约 walkableClimbWorld / 2 |
walkableRadiusWorld | 角色半径(半宽),决定墙体间隙 | 0.2–0.5 m |
walkableHeightWorld | 角色身高,低于此净高的区域排除 | 1.6–2.0 m |
walkableSlopeAngleDegrees | 可走最大坡度 | 35–50° |
walkableClimbWorld | 可上下的最大台阶高度 | 0.3–0.5 m |
minRegionArea | 保留的最小孤立区域(体素) | 4–16 |
mergeRegionArea | 小于此并入邻居的区域(体素) | 8–32 |
maxSimplificationError | 边简化容差,越大网格越简 | 1–2 |
maxEdgeLength | 多边形边超长则拆分 | 8–24 |
maxVerticesPerPoly | 每个多边形最大顶点数 | 3–6 |
这些是起步启发式,官方强烈建议配合库自带的 debug 可视化工具逐关微调,而不是一套参数通吃。

五、集成口径与注意事项
- 坐标约定:NavCat 遵循 OpenGL 约定——右手坐标系、索引逆时针环绕;若你的环境用其他坐标系,进出库都要做坐标变换。
- 外部导入要求:导入外部制作的 navmesh 时,多边形顶点必须共享(相邻多边形共用顶点),不能各自重复。
- 输入几何要干净:README 提醒不要把树叶、小装饰道具这类不该参与可走面生成的几何喂进去。
- 多 agent 腐蚀:
erodeAndMarkWalkableAreas支持按不同半径分级标记窄道,官方特别提示cellSize要足够小才能表达窄通道,阈值 1–2 体素容易产生坏区域,可改用buildRegionsMonotone。
六、局限与口径
- 离线烘焙假设:管线面向静态关卡的构建期生成,动态障碍物、实时重建不在默认能力内;tiled navmesh 支持分块重烤与流式,但需要自己组织。
- 参数调优成本:没有”默认参数即完美”,窄门、斜坡、台阶的取舍高度依赖 debug 迭代,小团队容易在调参上耗时。
- 非 recast 直移植:行为细节与 C#/C++ 版不完全等价,迁移经验不能照搬;官方称核心思路同源但实现独立。
- 人群模拟是示例层:图中的避让效果来自示例与查询 API 组合,不是内置的成熟 crowd 避障引擎,复杂群体行为需自行扩展。
- 性能数字官方未给统一基准:不同几何规模下的生成耗时与查询延迟需在自己关卡上实测,本文不引用具体毫秒数。
七、适用与不适用
适合:Three.js/Babylon.js 的室内 3D 导航、网页展厅/沙盘的角色行走、轻量 3D 游戏寻路;需要 navmesh 数据 JSON 序列化、可在前后端之间传递的场景;希望引擎无关、自行控制渲染的团队。
不适合:开放大世界的实时动态寻路;需要成熟 crowd 避障/群体调度引擎;2D 俯视角(格子图更简单);追求零配置开箱即用的非技术团队。
八、它意味着什么
NavCat 的意义在于把 recast 这套经过二十年验证的体素化 navmesh 方法论落到了纯 JS,让浏览器 3D 场景第一次有了不依赖 WASM 移植、可 tree-shake、引擎无关的生成与查询工具。它不是游戏引擎级的完整寻路中间件,而是一块扎实的基础设施积木,适合有能力自行调参与扩展的团队。