CD Code Digest Hub /CS:GO

导航网格(Navigation Mesh)

46 分钟阅读 10 个章节 2 张图

导航网格是服务端对“玩家能走到哪里”的离线近似:地图被切成一组轴对齐矩形 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
每个 GameFrameTheNavMesh->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::DLLShutdowndelete TheNavMeshgame/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_incomingConnectgame/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、EXPOSEDgame/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 加罚 ×20game/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魔数 0xFEEDFACEgame/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
5m_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
7SaveCustomDataPreArea(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
10SaveCustomData(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 的流程与失败路径:

  1. 清空旧网格,通知 GameRules()->OnNavMeshLoad():game/server/nav_file.cpp:1383-1390。
  2. PC 上先从 MOD 路径读独立 .nav,失败再从 BSP 路径读嵌入在地图包内的副本(navIsInBsp);都失败返回 NAV_CANT_ACCESS_FILE。主机平台用 GAME 路径并支持 LZMA 压缩:game/server/nav_file.cpp:1401-1435。
  3. 魔数错误返回 NAV_INVALID_FILE;版本高于 16 返回 NAV_BAD_FILE_VERSION:game/server/nav_file.cpp:1437-1451。
  4. BSP 大小与记录值不符时只置 m_isOutOfDate 并打印开发者警告,继续加载;监听服务器的主机玩家出生时会收到居中提示:game/server/nav_file.cpp:1464-1494、game/server/cstrike15/cs_player.cpp:1265-1268。
  5. 区域数为 0 返回 NAV_INVALID_FILE;否则用 CreateArea() 逐个创建并读入、计算外包盒、建网格:game/server/nav_file.cpp:1513-1555。
  6. 读梯子(v6 以下改为现场 BuildLadders)、标记楼梯、读派生类数据、PostLoad:game/server/nav_file.cpp:1561-1594。
  7. 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_incrementalBeginGeneration() / BeginGeneration(INCREMENTAL_GENERATION)game/server/nav_mesh.cpp:2364-2382
nav_analyze / nav_analyze_scripted仅在 nav_edit 1 时 BeginAnalysis();后者已分析则直接 quit,完成后也 quitgame/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::ComputePathgame/server/cstrike15/bot/cs_bot.h:1964-2130
ApproachAreaCost纯距离,临时封锁列表中的区域返回 -1ComputeApproachAreasgame/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
最早到达时间合作任务模式下恒为 0game/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 与避障物实体。