导航网格(Navigation Mesh)
导航网格是服务端对“玩家能走到哪里”的离线近似:地图被切成一组轴对齐矩形 CNavArea,区域之间用有向连接和梯子相连,并附带隐藏点、遭遇点、地点名(Place)与“最早到达时间”等战术注记。CS:GO 的 Bot 用它寻路、挑选埋伏点、预判敌人来向;人质、鸡、死亡竞赛随机出生点和 HUD 上的位置名也依赖它。本页解释网格的数据模型、.nav 文件格式与加载时机、分阶段生成流程、空间查询与 A* 寻路,以及 CS:GO 的扩展和运行时使用者。
相关页面:Bot · Bot 决策 · 主循环与 Tick · 地图加载与碰撞追踪 · 投掷物与 C4 · 构建系统与物理组织
相关源码文件#
- 通用实现(
server_base.vpc的 “Nav Mesh” 文件夹):game/server/nav.h、game/server/nav_mesh.h、game/server/nav_mesh.cpp、game/server/nav_area.h、game/server/nav_area.cpp、game/server/nav_file.cpp、game/server/nav_generate.cpp、game/server/nav_pathfind.h、game/server/nav_ladder.h、game/server/nav_mesh_factory.cpp;编辑与生成辅助nav_edit.cpp、nav_merge.cpp、nav_simplify.cpp、nav_node.cpp - CS:GO 扩展(
server_cstrike15_inc.vpc的 “Navigation Mesh” 文件夹):game/server/cstrike15/cs_nav_mesh.h、game/server/cstrike15/cs_nav_mesh.cpp、game/server/cstrike15/cs_nav_area.h、game/server/cstrike15/cs_nav_area.cpp、game/server/cstrike15/cs_nav_generate.cpp、game/server/cstrike15/cs_nav_pathfind.h、game/server/cstrike15/cs_nav_path.h - 主要使用者:
game/server/cstrike15/bot/cs_bot_pathfind.cpp、game/server/cstrike15/bot/cs_bot.h(PathCost)、game/server/cstrike15/bot/cs_bot_manager.cpp(炸弹点/营救区 Zone)、game/shared/cstrike15/bot/bot_hide.cpp(隐藏点搜索)
1. 构建归属与生命周期#
- 通用网格代码由
server_base.vpc编入服务端:game/server/server_base.vpc:872-894;CS:GO 额外编入cs_nav_area/generate/mesh/path等文件:game/server/server_cstrike15_inc.vpc:345-356。目录里的cs_nav_file.cpp、cs_nav_node.cpp、cs_nav_edit.cpp和game/shared/cstrike15/bot/nav_path.cpp不在任何 CS:GO 工程清单中(本次在全部.vpc中只检索到cs_nav_path.cpp的引用),是未编译的旧副本。旁证:cs_nav_file.cpp与nav_file.cpp都定义了CNavArea::ComputeEarliestOccupyTimes,同时编译会重复定义:game/server/cstrike15/cs_nav_file.cpp:755、game/server/nav_file.cpp:834。 - 全局单例
TheNavMesh:game/server/nav_mesh.cpp:35。NavMeshFactory在CSTRIKE_DLL下返回CSNavMesh:game/server/nav_mesh_factory.cpp:26-37;CSNavMesh::CreateArea使所有区域实际为CCSNavArea:game/server/cstrike15/cs_nav_mesh.cpp:69-72。 - 构造函数读取地点名数据库
NavPlace.db(GAME路径),并监听round_start、round_start_pre_entity、break_prop、break_breakable等事件:game/server/nav_mesh.cpp:61-80、game/server/nav_mesh.cpp:1090-1094。
| 时机 | 调用 | 证据 |
|---|---|---|
CServerGameDLL::DLLInit 末尾 | TheNavMesh = NavMeshFactory() | game/server/gameinterface.cpp:684-685 |
CServerGameDLL::ServerActivate(实体生成之后) | TheNavMesh->Load()、OnServerActivate(),随后 TheBots->ServerActivate();Load 的返回值被忽略;整段在 #ifndef _XBOX 内 | game/server/gameinterface.cpp:1375-1383 |
每个 GameFrame | TheNavMesh->Update():生成中则以 0.03 秒预算推进生成并直接返回;否则测试阻挡区域、更新避障物、绘制编辑/调试视图 | game/server/gameinterface.cpp:1472、game/server/nav_mesh.cpp:251-300 |
round_start 事件 | OnRoundRestart(),再对全部区域和梯子广播 | game/server/nav_mesh.cpp:398-426 |
CServerGameDLL::DLLShutdown | delete TheNavMesh | game/server/gameinterface.cpp:1008-1013 |
CSNavMesh::Update 在调用基类之前先维持鸡群数量和死亡竞赛随机出生点:game/server/cstrike15/cs_nav_mesh.cpp:233-240。
2. 数据模型#
| 概念 | 代码实体 | 要点 | 证据 |
|---|---|---|---|
| 区域 | CNavArea(热数据在基类 CNavAreaCriticalData) | 由西北角 m_nwCorner、东南角 m_seCorner 和另两个隐式角高度 m_neZ/m_swZ 定义,XY 为矩形、四角高度可不同 | game/server/nav_area.h:210-220、game/server/nav_area.h:257 |
| 连接 | NavConnect、m_connect[NUM_DIRECTIONS] | 按东南西北四方向保存有向邻接;没有回边的单向连接在加载后登记到目标区域的 m_incomingConnect | game/server/nav_area.h:64、game/server/nav_area.h:239、game/server/nav_area.h:354-363、game/server/nav_file.cpp:1647-1697 |
| 梯子 | CNavLadder、m_ladder[] | 顶/底坐标、长度、宽度;顶部前/左/右/后与底部各连一个区域 | game/server/nav_ladder.h:23-67、game/server/nav_area.h:240 |
| 隐藏点 | HidingSpot | 标志位 IN_COVER、GOOD_SNIPER_SPOT、IDEAL_SNIPER_SPOT、EXPOSED | game/server/nav_area.h:112-135 |
| 遭遇点 | SpotEncounter / SpotOrder | 穿过本区域的一条 from→to 路径段,以及沿途按出现顺序需要盯防的隐藏点 | game/server/nav_area.h:178-202 |
| 地点 | Place(unsigned int,0 = UNDEFINED_PLACE) | 区域所属的命名分组 | game/server/nav.h:63-68、game/server/nav_area.h:670 |
| 战术注记 | m_earliestOccupyTime[]、m_lightIntensity[]、m_potentiallyVisibleAreas | 每队从出生点最早到达的时间、四角光照、潜在可见区域集合 | game/server/nav_area.h:692-699、game/server/nav_area.h:738 |
| 动态状态 | m_danger[]、m_playerCount[]、m_isBlocked[]、IsDamaging() | 按队伍保存的危险度、人数、阻挡标志,以及火焰造成的“伤害区” | game/server/nav_area.h:221-227、game/server/nav_area.h:349-350 |
| A* 临时字段 | m_totalCost、m_costSoFar、开放表指针、m_parent、m_parentHow | 搜索结果以父指针链表示 | game/server/nav_area.h:229-248、game/server/nav_area.h:431-453 |
按队伍的数组长度 MAX_NAV_TEAMS = 2,下标为 teamID % MAX_NAV_TEAMS:game/server/nav_area.h:19、game/server/nav_area.cpp:4116-4137。移动方式 NavTraverseType 前四项与四方向一致,另有梯子上/下、跳跃和电梯上/下:game/server/nav.h:112-140。
空间索引是 300 单位边长的二维网格 m_grid:AllocateGrid 按网格外包盒分配单元,AddNavArea 把区域放进它覆盖的每个单元并挂到按 ID 的哈希表:game/server/nav_mesh.cpp:64、game/server/nav_mesh.h:790-793、game/server/nav_mesh.cpp:433-490。
区域属性与几何常量#
NavAttributeType 是区域位标志:低位为引擎定义,NAV_MESH_FIRST_CUSTOM..LAST_CUSTOM 留给游戏,最高几位被阻挡门、电梯和 nav blocker 占用:game/server/nav.h:81-108。对 CS Bot 行为有直接影响的几种:
| 属性 | 含义(源码注释) | CS 中的读取方 | 证据 |
|---|---|---|---|
NAV_MESH_CROUCH | 必须蹲伏通过 | PathCost 加罚;路径跟随中接近时蹲下 | game/server/cstrike15/bot/cs_bot.h:2053-2066、game/server/cstrike15/bot/cs_bot_pathfind.cpp:1542-1580 |
NAV_MESH_JUMP | 必须跳跃(生成期标记) | PathCost 禁止 jump→jump 并加罚 | game/server/cstrike15/bot/cs_bot.h:1989-1993、game/server/cstrike15/bot/cs_bot.h:2068-2075 |
NAV_MESH_WALK / NAV_MESH_NO_JUMP | 必须行走 / 禁止跳跃 | 卡住时 Wiggle 的探测距离与是否允许跳 | game/server/cstrike15/bot/cs_bot_nav.cpp:766、game/server/cstrike15/bot/cs_bot_nav.cpp:832-838 |
NAV_MESH_AVOID | 除非其他路线更危险否则避开 | PathCost 加罚 ×20 | game/server/cstrike15/bot/cs_bot.h:2084-2089 |
NAV_MESH_NO_HOSTAGES | 人质不可进入 | 护送人质时 PathCost 返回 -1(不可通行) | game/server/cstrike15/bot/cs_bot.h:1994-1998 |
几何常量随 CSTRIKE_DLL 取值:采样步长 GenerationStepSize = 25、蹲跳高度 JumpCrouchHeight = 58、台阶高 StepHeight = 18、致死落差 DeathDrop = 200、ClimbUpHeight 等于蹲跳高度:game/server/nav.h:21-48。
3. .nav 文件格式与加载#
文件名由地图名推出(maps\<map>.nav,平台扩展名插在中间):game/server/nav_file.cpp:174-175、game/server/nav_file.cpp:1392-1394。CNavMesh::Save 写出的顺序如下,Load 按同一顺序读回:
| 顺序 | 字段 | 证据 |
|---|---|---|
| 1 | 魔数 0xFEEDFACE | game/server/nav.h:61、game/server/nav_file.cpp:1086-1087 |
| 2 | 版本号 NavCurrentVersion = 16(注释列出 1–16 每版新增内容,如 v5 地点、v6 梯子、v14 是否已分析、v16 网格可见性) | game/server/nav_file.cpp:39、game/server/nav_file.cpp:1089-1108 |
| 3 | 子版本号,归派生类所有;CSNavMesh 返回 1(注释:加入 ApproachArea 数据) | game/server/nav_file.cpp:1110-1112、game/server/cstrike15/cs_nav_mesh.cpp:100-104 |
| 4 | 生成时 BSP 文件的字节数 | game/server/nav_file.cpp:1114-1119 |
| 5 | m_isAnalyzed 标志 | game/server/nav_file.cpp:1121-1122 |
| 6 | 地点目录:条目数、每条“长度 + 名称”、是否有未命名区域;区域中只存目录下标,地点 ID 顺序变化不会使文件失效 | game/server/nav_file.cpp:42-52、game/server/nav_file.cpp:122-140、game/server/nav_file.cpp:1127-1137 |
| 7 | SaveCustomDataPreArea(CS 未覆盖,为空) | game/server/nav_file.cpp:1139、game/server/nav_mesh.h:300 |
| 8 | 区域数 + 每个区域:ID、属性、角点、连接、隐藏点、遭遇路径、地点下标、梯子 ID、最早到达时间、光照、可见区域集合;CCSNavArea 再追加 approach 区域列表 | game/server/nav_file.cpp:1144-1156、game/server/nav_file.cpp:217-385、game/server/cstrike15/cs_nav_area.cpp:68-99 |
| 9 | 梯子数 + 每个梯子 | game/server/nav_file.cpp:1161-1172 |
| 10 | SaveCustomData(CSNavMesh 为空实现) | game/server/nav_file.cpp:1177、game/server/cstrike15/cs_nav_mesh.cpp:110-122 |
注意基类注释称 v15 “removed approach areas”,但 CS 通过子版本 1 在每个区域末尾自行保存/读取 approach 数据;版本低于 15 的文件走 CCSNavArea::LoadLegacy,未知子版本返回 NAV_INVALID_FILE:game/server/nav_file.cpp:1106、game/server/cstrike15/cs_nav_area.cpp:101-144。
CNavMesh::Load 的流程与失败路径:
- 清空旧网格,通知
GameRules()->OnNavMeshLoad():game/server/nav_file.cpp:1383-1390。 - PC 上先从
MOD路径读独立.nav,失败再从BSP路径读嵌入在地图包内的副本(navIsInBsp);都失败返回NAV_CANT_ACCESS_FILE。主机平台用GAME路径并支持 LZMA 压缩:game/server/nav_file.cpp:1401-1435。 - 魔数错误返回
NAV_INVALID_FILE;版本高于 16 返回NAV_BAD_FILE_VERSION:game/server/nav_file.cpp:1437-1451。 - BSP 大小与记录值不符时只置
m_isOutOfDate并打印开发者警告,继续加载;监听服务器的主机玩家出生时会收到居中提示:game/server/nav_file.cpp:1464-1494、game/server/cstrike15/cs_player.cpp:1265-1268。 - 区域数为 0 返回
NAV_INVALID_FILE;否则用CreateArea()逐个创建并读入、计算外包盒、建网格:game/server/nav_file.cpp:1513-1555。 - 读梯子(v6 以下改为现场
BuildLadders)、标记楼梯、读派生类数据、PostLoad:game/server/nav_file.cpp:1561-1594。 PostLoad把 ID 绑定为指针、v8 以下补算最早到达时间、计算单向入边、校验连接,最后才置m_isLoaded = true:game/server/nav_file.cpp:1619-1712。CSNavMesh::PostLoad先设定期望鸡数(枪械竞赛死亡竞赛为 10,否则取地图信息)、合作模式下删除地图自带的 DM 出生点并重建随机出生点:game/server/cstrike15/cs_nav_mesh.cpp:252-273。
辅助命令:nav_save、nav_load(需服务器管理员、FCVAR_CHEAT):game/server/nav_mesh.cpp:2076-2105;nav_check_file_consistency 扫描 maps/*.bsp 报告缺失或过期的 .nav:game/server/nav_file.cpp:1254-1286。
4. 生成流程(分帧增量)#
入口:
| 入口 | 行为 | 证据 |
|---|---|---|
nav_generate / nav_generate_incremental | BeginGeneration() / BeginGeneration(INCREMENTAL_GENERATION) | game/server/nav_mesh.cpp:2364-2382 |
nav_analyze / nav_analyze_scripted | 仅在 nav_edit 1 时 BeginAnalysis();后者已分析则直接 quit,完成后也 quit | game/server/nav_mesh.cpp:2386-2425 |
| 添加 Bot 时网格未加载 | PC 上每张地图最多自动触发一次 BeginGeneration(),本次添加失败;主机平台直接拒绝 | game/server/cstrike15/bot/cs_bot_manager.cpp:1037-1060 |
CCSGameRules::DoCoopSpawnAndNavInit | 网格未加载时开始生成,否则把 bot_quota 设为 20;本次只检索到其定义与声明,未找到调用方 | game/shared/cstrike15/cs_gamerules.cpp:11555-11593、game/shared/cstrike15/cs_gamerules.h:1453 |
BeginGeneration 触发 nav_generate 事件、执行 bot_kick(非 L4D 分支),增量模式强制 nav_quicksave 1;然后清空网格、建梯子、从出生点实体播种。出生点类名由 Bot 管理器设置:普通模式 info_player_terrorist,合作任务 info_enemy_terrorist_spawn;没有种子时打印错误并放弃:game/server/nav_generate.cpp:3402-3469、game/server/nav_generate.cpp:3378-3395、game/server/cstrike15/bot/cs_bot_manager.cpp:496-504。
证据:状态枚举与模式枚举 game/server/nav_mesh.h:916-939;BeginGeneration 进入 SAMPLE_WALKABLE_SPACE,BeginAnalysis 进入 FIND_HIDING_SPOTS 且模式为 GENERATE_ANALYSIS_ONLY game/server/nav_generate.cpp:3431-3433、game/server/nav_generate.cpp:3536-3538;采样与建区 game/server/nav_generate.cpp:3642-3707;隐藏点、遭遇点、狙击点 game/server/nav_generate.cpp:3710-3800;CSNavMesh::IsMeshVisibilityGenerated 返回 false game/server/cstrike15/cs_nav_mesh.h:67;可见性与最早到达时间 game/server/nav_generate.cpp:3804-3864;光照分支 game/server/nav_generate.cpp:3852-3864、game/server/nav_generate.cpp:3880-3961;CUSTOM 与保存 game/server/nav_generate.cpp:3967-4040。
要点:
- 每个状态都在
UpdateGeneration(maxTime)内按时间片推进,超时立即返回,下一帧继续——这就是“增量”的含义:game/server/nav_generate.cpp:3629-3659、game/server/nav_generate.cpp:3710-3729。 CreateNavAreasFromNodes先用从大到小的尝试尺寸把采样节点贪心覆盖成矩形,再依次连接、标记玩家碰撞区与跳跃区、合并、切分悬垂、方整化、标记楼梯、处理障碍顶、修补,最后连接梯子;CSTRIKE_DLL下保留跳跃区(注释:CSBots use jump areas):game/server/nav_generate.cpp:3274-3373。CUSTOM阶段对每个区域调用CustomAnalysis;CCSNavArea在这里计算 approach 区域(nav_quicksave为真时跳过)。CS 下nav_quicksave默认 0,注释称“CSBots 需要这些数据”:game/server/nav_generate.cpp:3967-3995、game/server/cstrike15/cs_nav_area.cpp:184-187、game/server/cstrike15/cs_nav_generate.cpp:81-86、game/server/nav_mesh.cpp:39-45。- 最早到达时间在 CS 下按“从各
info_player_terrorist/info_player_counterterrorist出生点出发、以 240 单位/秒沿最短路径行进”分队计算,上限 120 秒;非 CS 构建全部置 0:game/server/nav_file.cpp:834-892。 SAVE_NAV_MESH:完整生成或纯分析才置m_isAnalyzed;保存后若nav_analyze_scripted则退出,否则非增量模式ChangeLevel重启当前地图:game/server/nav_generate.cpp:4005-4040。
5. 空间查询#
| API | 行为 | 证据 |
|---|---|---|
GetNavArea(pos, beneathLimit = 120, checkLOS) | 只查 pos 所在网格单元;在 XY 覆盖 pos + 5 的区域中,取不高于该点、且不低于 pos.z - beneathLimit 的最高者;可选向下追踪确认中间没有遮挡 | game/server/nav_mesh.cpp:638-701 |
GetNearestNavArea(pos, anyZ, maxDist = 10000, checkLOS, checkGround = true) | 两个检查都关时先试 GetNavArea;否则取地面高度(checkGround 且无地面则返回 NULL),从所在单元向外按环扫描,跳过被阻挡区域,以区域最近点到 pos 的 3D 距离取最小;找到后再多扫一圈防止边界误差 | game/server/nav_mesh.cpp:792-961 |
GetNavAreaByID | 哈希表查找(加载时用来把 ID 绑定为指针) | game/server/nav_mesh.cpp:994、game/server/nav_mesh.cpp:472-490 |
ForAllAreasOverlappingExtent / ForAllAreasInRadius | 基于网格单元的范围遍历;网格为空时打印 “Query before nav mesh is loaded!” | game/server/nav_mesh.h:558-660 |
GetPlace / PlaceToName / NameToPlace | 坐标或名称与 Place 互转 | game/server/nav_mesh.h:333-336 |
GetGroundHeight / GetSimpleGroundHeight | 追踪求地面高度 | game/server/nav_mesh.h:340-341、game/server/nav_mesh.cpp:1297 |
6. 寻路:NavAreaBuildPath 与代价函数#
NavAreaBuildPath<CostFunctor> 是一个模板化 A*:它不构造路径对象,只在区域上留下父指针,调用方从目标沿 GetParent() 回溯:game/server/nav_pathfind.h:95-109。
- 目标区域被阻挡时退化为“尽量接近
goalPos”;起点即终点时返回平凡路径;启发值是区域中心到目标点的直线距离:game/server/nav_pathfind.h:121-152、game/server/nav_pathfind.h:353-365。 - 扩展顺序:四个方向的地面连接 → 向上梯子(只取顶部前/左/右出口,注释称背后出口难以到达)→ 向下梯子 → 电梯。电梯分支当前直接
break,即电梯不参与扩展:game/server/nav_pathfind.h:216-318。 - 被阻挡区域被跳过;代价函数返回负值表示死路;
maxPathLength非零时超长分支被剪掉;全程记录离目标最近的区域,搜索失败时通过closestArea返回,供调用方走“半程路径”:game/server/nav_pathfind.h:320-391。
| 代价函数 | 规则 | 使用方 | 证据 |
|---|---|---|---|
ShortestPathCost | 距离(梯子用梯长、连接自带长度优先)累加;蹲伏区 ×20、跳跃区 ×5、伤害区 ×100 的附加罚分 | 最早到达时间、合作模式出生点排序、Bot 选最近炸弹点 | game/server/nav_pathfind.h:39-93、game/shared/cstrike15/cs_gamerules.cpp:7351-7368、game/server/cstrike15/bot/states/cs_bot_idle.cpp:320-324 |
PathCost | 危险度、坠落伤害、属性罚分、随机扰动、队友密度等,详见 Bot 决策 | CCSBot::ComputePath | game/server/cstrike15/bot/cs_bot.h:1964-2130 |
ApproachAreaCost | 纯距离,临时封锁列表中的区域返回 -1 | ComputeApproachAreas | game/server/cstrike15/cs_nav_generate.cpp:34-73 |
NavAreaTravelDistance 有区域版和坐标版两个包装,都先跑 NavAreaBuildPath,再沿父链累加中心距离,不可达返回 -1:game/server/nav_pathfind.h:398-411、game/server/cstrike15/cs_nav_pathfind.h:22-63。SearchSurroundingAreas 从起点沿连接向外扩张,由仿函数返回值决定是否继续、受 maxRange 限制(该模板版注释标为 DEPRECATED),Bot 用它收集附近隐藏点:game/server/nav_pathfind.h:476-540、game/shared/cstrike15/bot/bot_hide.cpp:182-230。
人质和鸡不用 Bot 的路径数组,而是用 cs_nav_path.h 中的 CCSNavPath + CNavPathFollower:game/server/cstrike15/cs_nav_path.h:24、game/server/cstrike15/cs_nav_path.h:66-90、game/server/cstrike15/cs_nav_path.h:204、game/server/cstrike15/hostage/cs_simple_hostage.h:193-198、game/server/cstrike15/hostage/cs_simple_hostage.cpp:977、game/server/cstrike15/Effects/chicken.h:135-140、game/server/cstrike15/Effects/chicken.cpp:756。
7. CS:GO 扩展#
| 扩展点 | 行为 | 证据 |
|---|---|---|
| approach 区域 | 每个 CCSNavArea 最多 16 个 ApproachInfo(here/prev/next 及其移动方式);注释定义为“玩家进出本区域局部邻域的位置”。生成时以区域中心的近似眼高为视点,对每个远处区域分“由眼向外 / 由外向眼”两个方向寻路求并集;运行时 Bot 用当前区域的 approach 信息算出入口点,并“弯折”视线找到可观察的位置 | game/server/cstrike15/cs_nav_area.h:61-85、game/server/cstrike15/cs_nav_generate.cpp:75-130、game/server/cstrike15/bot/cs_bot_nav.cpp:845-895 |
| 阻挡判定 | CCSNavArea::IsBlocked 与 UpdateBlocked 只在合作任务模式下调用基类,其余模式一律“未阻挡”;头文件注释称普通模式下 nav blocker 会导致 Bot 回合开始时丢失路径、原因未查明 | game/server/cstrike15/cs_nav_area.cpp:213-222、game/server/cstrike15/cs_nav_area.h:53-58 |
| 危险度衰减 | 每秒 1/120(一次死亡约两分钟遗忘),枪械竞赛爆破模式 1/30;Bot 死亡时在死亡点 500 单位内增加本队危险度 | game/server/cstrike15/cs_nav_area.cpp:190-202、game/server/cstrike15/bot/cs_bot.cpp:187-196、game/server/nav_mesh.cpp:1392-1450 |
| 最早到达时间 | 合作任务模式下恒为 0 | game/server/cstrike15/cs_nav_area.cpp:205-211 |
| 炸弹点 / 营救区(Zone) | Bot 管理器在激活时扫描 func_bomb_target、info_bomb_target、func_hostage_rescue 等实体确定场景类型,按实体包围盒(旧式点实体取 ±256)加 Z 方向 ±50 收集重叠区域;缺营救区的人质图用 info_player_start 代替;仅在网格已加载时执行 | game/server/cstrike15/bot/cs_bot_manager.cpp:1606-1644、game/server/cstrike15/bot/cs_bot_manager.cpp:1651-1792 |
| 地点名 | 玩家约每 30 tick 用 GetNavArea(WorldSpaceCenter, 1000, LOS) 更新 m_szLastPlaceName(注释:用于 HUD 语音界面);玩家资源实体按炸弹点所在区域的地点名(BombsiteA/Bombsite → A,否则 B;无地点名时按出现顺序)确定 A/B 中心 | game/server/cstrike15/cs_player.cpp:6813-6838、game/server/cstrike15/cs_player_resource.cpp:307-339 |
| 鸡群 / DM 出生点 | 随机挑选足够大且双方都看不见的区域生成鸡;mp_randomspawn 时在随机区域(或 mp_guardian_target_site 指定炸弹点的 Zone 区域)生成最多 50 个 DM 出生点 | game/server/cstrike15/cs_nav_mesh.cpp:153-200、game/server/cstrike15/cs_nav_mesh.cpp:282-365 |
| 火焰 | CInferno 把被火覆盖的区域标为伤害区,代价函数据此绕行 | game/server/cstrike15/Effects/inferno.cpp:807-830 |
8. 运行时使用者一览#
证据:Bot 每次 Update 用 GetNavArea 维护当前/最近区域,寻路从 m_lastKnownArea 出发,噪声落在网格外则忽略:game/server/cstrike15/bot/cs_bot_update.cpp:332-344、game/server/cstrike15/bot/cs_bot_pathfind.cpp:1915-1961、game/server/cstrike15/bot/cs_bot_event.cpp:78-89;Zone 见上表;人质 GetNavArea 跟踪所在区域:game/server/cstrike15/hostage/cs_simple_hostage.cpp:1231-1237;玩家与规则、火焰、出生点维护见第 6、7 节引用。
约束与边界#
- 内容缺失:仓库没有任何
.nav、NavPlace.db(本次rg --files -g "*.db"无结果),所以运行行为完全取决于外部内容。 - 缺少
.nav时:ServerActivate忽略Load的错误码,m_isLoaded保持 false:game/server/gameinterface.cpp:1375-1378、game/server/nav_file.cpp:1427-1434。此后bot_quota维护会调用BotAddCommand,在 PC 上触发一次自动生成;生成开始时踢掉所有 Bot、完成后保存并重启地图:game/server/cstrike15/bot/cs_bot_manager.cpp:1551-1557、game/server/cstrike15/bot/cs_bot_manager.cpp:1037-1060、game/server/nav_generate.cpp:3421、game/server/nav_generate.cpp:4037-4040。生成期间 Bot 的Upkeep与配额维护直接返回:game/server/cstrike15/bot/cs_bot_update.cpp:33-34、game/server/cstrike15/bot/cs_bot_manager.cpp:1440-1441。 - 非 CS 默认差异:
StepHeight、DeathDrop、JumpCrouchHeight、nav_quicksave默认值、CreateNavAreasFromNodes是否保留跳跃区都受CSTRIKE_DLL/INFESTED_DLL宏影响(见第 2、4 节引用)。基类CBaseCombatCharacter::UpdateLastKnownArea在CSTRIKE_DLL下被整体编译掉:game/server/basecombatcharacter.cpp:3717-3771。 - 人数统计基本不维护(推断):CS 中只检索到 Bot 路径“放弃”分支调用
IncrementPlayerCount:game/server/cstrike15/bot/cs_bot_pathfind.cpp:1825-1836;玩家的位置名更新只赋值m_lastNavArea、不计数:game/server/cstrike15/cs_player.cpp:6824-6827。因此PathCost中“队友密度”项多数时候读到 0。未运行验证。 - 危险度扩散公式(推断):
IncreaseDangerNearby对相邻区域加amount * cost / maxRadius,离死亡点越远增量越大,与注释“危险源”的直觉相反,可能是实现缺陷:game/server/nav_mesh.cpp:1430-1446。 - 未使用的参数/分支:
GetNearestNavArea的anyZ参数在函数体中没有被读取:game/server/nav_mesh.cpp:792-961;电梯扩展是空分支(第 6 节);CS 跳过网格可见性计算,潜在可见区域集合在生成时被清空:game/server/nav_generate.cpp:3787-3798。 - 证据不足:
CSNavMesh构造函数只初始化m_desiredChickenCount,m_desiredDMSpawns仅在PostLoad → ResetDMSpawns中赋值:game/server/cstrike15/cs_nav_mesh.cpp:59-62、game/server/cstrike15/cs_nav_mesh.cpp:282-293;网格未加载且开启mp_randomspawn时的行为无法从静态阅读确定。DoCoopSpawnAndNavInit引用的nav_generate_fast_for_tilegen在本仓库中没有定义(只有ConVarRef):game/shared/cstrike15/cs_gamerules.cpp:11582-11584。 - 本页未展开:
nav_edit.cpp的交互编辑器(nav_edit 1与数十条nav_*编辑命令,game/server/nav_mesh.cpp:1463-1929)、合并/简化/悬垂切分算法细节、隐藏点/遭遇点/狙击点的具体计算(game/server/nav_area.cpp:3677、game/server/nav_area.cpp:3902、game/server/nav_area.cpp:4059)、光照采样、CNavPathFollower的内部实现、nav blocker 与避障物实体。