车辆架构:CVehicle 类层次、工厂、模型元数据、人口与帧内时序
本页是 08-vehicles/ 的父页面,解释 GTAV 里“车辆”这一实体在 C++ 层如何组织:VehicleType 枚举如何驱动类层次与类型判断、CVehicleFactory 如何创建/销毁车辆并挂接音频实体与 gadget、SetModelId 如何把模型信息(CVehicleModelInfo)、操控数据(CHandlingData)、物理实例与座位/门/轮子组装起来、vehiclelayouts/carcols/vehicles.meta 等数据驱动配置分别落在哪些类上、车辆人口(CVehiclePopulation)如何以“生成链接 + 异步作业 + 单周期限额”生成与回收车辆,以及一辆车在一帧内被哪些系统按什么顺序处理。
车辆代码规模很大:Vehicle.cpp 约 38,600 行、vehicle.h 约 3,470 行(主类声明从第 401 行开始)src/dev_ng/game/Vehicles/vehicle.h:401-411。物理/操控/损坏与 LOD 细节见子页 vehicle-physics-handling-damage.md;车辆 AI、路网、交通灯、录像与 GPS 见 vehicle-ai-and-road-network.md。
背景与相关页面#
- 实体基类链(
CEntity→CDynamicEntity→CPhysical)、CGameWorld与场景更新:见 ../05-world/world-and-entities.md。 - 模型信息体系总览(
CBaseModelInfo、vehicles.meta的装载入口、HD 请求):见 ../05-world/model-info.md。本页只展开车辆领域字段。 gameSkeleton注册与主循环、池与gameconfig.xml:见 ../01-runtime/game-skeleton-and-main-loop.md、../01-runtime/memory-and-pools.md。- 线程模型与
sysDependency/sysTaskManager:见 ../01-runtime/threading-and-render-thread.md。 - ped 侧上下车任务、驾驶动作(
CTaskEnterVehicle等):归 ../07-ai-tasks/task-catalogue.md;ped 人口与 PopCycle:../06-peds/ped-intelligence-and-population.md。 - 车辆网络对象
CNetObjVehicle:归 ../17-network-sync/network-object-model.md。 - 车辆音频(
audVehicleAudioEntity族):归 ../13-audio/game-audio.md;车辆玻璃:归 ../12-vfx/decals-and-vfx-systems.md;车辆绘制处理器与 GPU 形变渲染:归 ../11-rendering/render-pipeline-and-phases.md。
相关源码文件#
| 文件 | 作用 |
|---|---|
game/Vehicles/vehicle.h、Vehicle.cpp | CVehicle 基类:控制输入、物理入口、LOD/dummy 转换、AI 挂接、每帧静态更新函数、池大小助手 |
game/Vehicles/VehicleDefines.h | 池/数组上限、健康值常量、eVehicleDummyMode、锁状态、任务树枚举 |
game/Vehicles/VehicleFactory.h、VehicleFactory.cpp | CVehicleFactory:池初始化、创建/销毁、拖车/货物、gadget 装配、调试 bank |
game/Vehicles/Automobile.*、Bike.*、Bmx.*、boat.cpp/Boat.h、Heli.*、Planes.*、Submarine.*、Trailer.*、train.*、AmphibiousAutomobile.*、DraftVehicle.* | 各具体车辆类 |
game/Vehicles/door.h、door.cpp | CCarDoor、CBouncingPanel |
game/Vehicles/VehicleGadgets.h、VehicleGadgets.cpp | CVehicleGadget 族、炮塔/武器/CVehicleWeaponMgr |
game/Vehicles/componentReserve.* | 座位/门组件预留(ped 进出车协调) |
game/Vehicles/vehiclepopulation.*、VehiclePopulationJob.cpp、VehPopUpdateDensities*.cpp、vehiclepopulation_shared.cpp | 车辆人口:生成链接、异步作业、生成/回收、复用池 |
game/Vehicles/cargen.*、cargens.psc | 车辆生成点(CTheCarGenerators) |
game/Vehicles/Metadata/* | vehiclelayouts 元数据(布局、座位、入口点、驾驶射击、爆炸信息等)与 AI 操控曲线 |
game/modelinfo/VehicleModelInfo*.h/.cpp/.psc、ModelSeatInfo.* | 车辆模型信息、颜色/改装/车牌/警笛/灯光、座位与入口点运行时表、CSeatManager |
game/vehicleAi/VehicleAILod*.*、vehicleAi/task/DeferredTasks.* | 车辆 AI/物理 LOD 与延迟(多线程)任务 |
game/core/game.cpp、game/scene/world/GameWorld.cpp、game/scene/scene.cpp、game/physics/physics.cpp | 注册点与帧内调用顺序 |
1. 组件地图#
| 概念 | 代码实体 | 职责 | 证据 |
|---|---|---|---|
| 车辆实体 | CVehicle : CPhysical | 所有车辆的公共状态与虚接口;构造函数 protected,只允许 CVehicleFactory 创建 | src/dev_ng/game/Vehicles/vehicle.h:401-411 |
| 类型标签 | VehicleType | 决定具体类、音频实体、玩家驾驶任务;枚举顺序被用来做区间判断 | src/dev_ng/game/modelinfo/VehicleModelInfo.h:169-199 |
| 工厂 | CVehicleFactory | 池初始化、Create/Destroy、拖车与货物、gadget 装配 | src/dev_ng/game/Vehicles/VehicleFactory.h:27-71 |
| 模型信息 | CVehicleModelInfo | 每个车型的静态数据(操控 ID、布局、改装套件、LOD、标志…) | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:196-353 |
| 操控数据 | CHandlingData + 子 handling | 物理/操控参数,按名字由模型引用 | src/dev_ng/game/Vehicles/Vehicle.cpp:4015-4017 |
| 座位/入口 | CModelSeatInfo、CEntryExitPoint、CSeatManager | 布局 → 骨骼索引映射;运行时乘员表 | src/dev_ng/game/modelinfo/ModelSeatInfo.h:157-346 |
| 门 | CCarDoor | 铰链/滑动门的驱动、闩锁、断裂、导航交互 | src/dev_ng/game/Vehicles/door.h:54-96 |
| 轮子 | CWheel(池化) | 悬挂、轮胎力、接触;数组指针在子类中 | src/dev_ng/game/Vehicles/Automobile.h:377 |
| 变速箱 | CTransmission | 档位、引擎扭矩、引擎健康 | src/dev_ng/game/Vehicles/vehicle.h:2580-2584 |
| 损坏 | CVehicleDamage / CVehicleDeformation | 各部位健康、形变纹理、爆炸 | src/dev_ng/game/Vehicles/vehicle.h:2580 |
| AI | CVehicleIntelligence + CVehicleTaskManager | 车辆自身的任务树(与 ped 任务树独立) | src/dev_ng/game/Vehicles/vehicle.h:2850-2852 |
| LOD | CVehicleAILod | AL_LodDummy/AL_LodSuperDummy/AL_LodTimeslicing 标志 | src/dev_ng/game/vehicleAi/VehicleAILod.h:31-71 |
| gadget/武器 | CVehicleGadget、CVehicleWeaponMgr | 拖钩、叉车、挖掘臂、炮塔、探照灯… | src/dev_ng/game/Vehicles/vehicle.h:2869-2870 |
| 人口 | CVehiclePopulation | 环境车辆生成/移除、复用池、警车/急救车生成 | src/dev_ng/game/Vehicles/vehiclepopulation.h:68 |
2. 类层次与 VehicleType#
2.1 枚举即类层次#
VehicleType 的排列刻意与继承关系对齐:VEHICLE_TYPE_CAR…VEHICLE_TYPE_AUTOGYRO 全部派生自 CAutomobile(含飞机、拖车、四轮摩托、两栖车、潜水车,以及经 CRotaryWingAircraft 派生的直升机/飞艇/旋翼机),BIKE/BICYCLE 派生自 CBike,BOAT/TRAIN/SUBMARINE 直接派生自 CVehicle src/dev_ng/game/modelinfo/VehicleModelInfo.h:169-199。CVehicle 的所有 InheritsFromXxx() 都是对 m_vehicleType 的区间或等值比较,注释提醒“飞机也继承自 CAutomobile” src/dev_ng/game/Vehicles/vehicle.h:543-576。因此改动该枚举顺序会悄然破坏全部类型判断——枚举头部注释也明确要求检查 CVehicle 的类型函数 src/dev_ng/game/modelinfo/VehicleModelInfo.h:171。
证据:CVehicle : CPhysical src/dev_ng/game/Vehicles/vehicle.h:401;CAutomobile/CQuadBike src/dev_ng/game/Vehicles/Automobile.h:38、src/dev_ng/game/Vehicles/Automobile.h:499;CBike src/dev_ng/game/Vehicles/Bike.h:38、CBmx src/dev_ng/game/Vehicles/Bmx.h:25;CBoat src/dev_ng/game/Vehicles/Boat.h:174;CRotaryWingAircraft/CHeli/CAutogyro/CBlimp src/dev_ng/game/Vehicles/Heli.h:72、src/dev_ng/game/Vehicles/Heli.h:391、src/dev_ng/game/Vehicles/Heli.h:643、src/dev_ng/game/Vehicles/Heli.h:681;CPlane src/dev_ng/game/Vehicles/Planes.h:296;CSubmarine/CSubmarineCar src/dev_ng/game/Vehicles/Submarine.h:166、src/dev_ng/game/Vehicles/Submarine.h:220;CTrailer src/dev_ng/game/Vehicles/Trailer.h:48;CTrain src/dev_ng/game/Vehicles/train.h:193;CAmphibiousAutomobile/CAmphibiousQuadBike src/dev_ng/game/Vehicles/AmphibiousAutomobile.h:14、src/dev_ng/game/Vehicles/AmphibiousAutomobile.h:103;CDraftVeh src/dev_ng/game/Vehicles/DraftVehicle.h:25。
说明:源码中没有 CBicycle 类——VEHICLE_TYPE_BICYCLE 由 CBmx 实现 src/dev_ng/game/Vehicles/VehicleFactory.cpp:2816-2818;CDraftVeh 只有在 ENABLE_DRAFT_VEHICLE 打开时才会被工厂创建 src/dev_ng/game/Vehicles/VehicleFactory.cpp:2840-2844。
2.2 各具体类要点#
| 类 | 关键特征(摘要) | 证据 |
|---|---|---|
CAutomobile | 4 轮+通用路面车辆;持有 WheelIntegrationState m_WheelIntegrator、门数组 m_aCarDoors[NUM_VEH_DOORS_MAX]、轮子指针数组 m_pCarWheels[NUM_VEH_CWHEELS_MAX];三段式 ProcessPhysics/2/3 | src/dev_ng/game/Vehicles/Automobile.h:251、src/dev_ng/game/Vehicles/Automobile.h:373-377、src/dev_ng/game/Vehicles/Automobile.cpp:3187 |
CBike / CBmx | 自带一扇“门”与独立轮子数组、独立积分器状态 | src/dev_ng/game/Vehicles/Bike.h:173、src/dev_ng/game/Vehicles/Bike.h:274-278 |
CBoat | 通过组合 CBoatHandling m_BoatHandling 实现水上逻辑(两栖车复用同一组件) | src/dev_ng/game/Vehicles/Boat.h:259、src/dev_ng/game/Vehicles/AmphibiousAutomobile.h:89 |
CSubmarine / CSubmarineCar | 物理、控制、预渲染全部转发给组合的 CSubmarineHandling | src/dev_ng/game/Vehicles/Submarine.h:174-211 |
CRotaryWingAircraft 族 | 旋翼速度/健康、飞行模型;CBlimp 派生自 CHeli | src/dev_ng/game/Vehicles/Heli.cpp:2051-2073 |
CPlane | 普通/垂直起降双飞行模型按比例混合、CAircraftDamage | src/dev_ng/game/Vehicles/Planes.cpp:2555-2597 |
CTrain | 沿 CTrainTrack 节点运动,车站状态机 TrainState | src/dev_ng/game/Vehicles/train.h:198-207 |
CTrailer | CTrailerLegs gadget、挂接约束 | src/dev_ng/game/Vehicles/Trailer.h:11、src/dev_ng/game/Vehicles/Trailer.h:48 |
物理与操控差异详见 vehicle-physics-handling-damage.md。
3. CVehicle 的组成#
CVehicle 以值成员持有 CVehicleDamage m_VehicleDamage、CTransmission m_Transmission、CHandlingData* pHandling(指向共享的只读操控数据)、CVehicleFlags m_nVehicleFlags,以及音频实体指针、主物理实例 fragInstGta* m_pVehicleFragInst 与可选的 dummy 实例 phInstGta* m_pDummyInst src/dev_ng/game/Vehicles/vehicle.h:2580-2595。AI、LOD、轮/门数组、武器管理器与 gadget 列表、座位管理器、组件预留管理器在类尾部 src/dev_ng/game/Vehicles/vehicle.h:2850-2878。
| 成员 | 说明 | 证据 |
|---|---|---|
m_vehControls(CVehControls) | 转向/副转向/油门/刹车/手刹/氮气/KERS 输入;默认手刹为 true | src/dev_ng/game/Vehicles/vehicle.h:97-130 |
m_ppWheels / m_nNumWheels / m_WheelIndexById | 指向子类轮子数组;NUM_VEH_CWHEELS_MAX = 10 | src/dev_ng/game/Vehicles/vehicle.h:2856-2859、src/dev_ng/game/Vehicles/VehicleDefines.h:19 |
m_pDoors / m_nNumDoors | 指向子类门数组;NUM_VEH_DOORS_MAX = 9 | src/dev_ng/game/Vehicles/vehicle.h:2861-2864、src/dev_ng/game/Vehicles/VehicleDefines.h:20 |
m_pIntelligence / m_vehicleAiLod | 车辆 AI 与 LOD 状态 | src/dev_ng/game/Vehicles/vehicle.h:2851-2852 |
m_SeatManager / m_ComponentReservationMgr | 乘员表与座位/门预留 | src/dev_ng/game/Vehicles/vehicle.h:2876-2878 |
m_fTimeSinceLastAiUpdate | 时间片 LOD 下累积的 AI 步长 | src/dev_ng/game/Vehicles/vehicle.h:2586-2589 |
IsDummy()/IsSuperDummy() 直接读取 LOD 标志 src/dev_ng/game/Vehicles/vehicle.h:992-993;dummy 相关虚函数(TryToMakeIntoDummy、SwitchToDummyPhysics 等)在基类声明 src/dev_ng/game/Vehicles/vehicle.h:996-1029,机制见物理子页第 8 节。
4. 内存与池#
| 资源 | 容量 / 规则 | 证据 |
|---|---|---|
CVehicle 池 | VEHICLE_POOL_SIZE 不再是常量 200,而是从 gameconfig.xml 的 "Vehicles" 池读取(CPoolHelpers::SetVehiclePoolSize),未设置即 TrapZ | src/dev_ng/game/Vehicles/VehicleDefines.h:11-17、src/dev_ng/game/Vehicles/Vehicle.cpp:32152-32174、src/dev_ng/game/system/system.cpp:966 |
CVehicle 分配方式 | REGISTER_POOL_ALLOCATOR + 带堆的池:堆大小 VEHICLE_HEAP_SIZE 在 360/PS3 为 464KB,其它平台 ×10;子类大小不同,所以是“变长池” | src/dev_ng/game/Vehicles/vehicle.h:426、src/dev_ng/game/Vehicles/VehicleFactory.cpp:1443-1453 |
| 音频实体池 | 同样按 VEHICLE_POOL_SIZE 个、488KB(×10)堆 | src/dev_ng/game/Vehicles/VehicleFactory.cpp:1461-1467 |
CWheel 池 | WHEEL_POOL_SIZE = VEHICLE_POOL_SIZE * 5,spillover 0.33 | src/dev_ng/game/Vehicles/VehicleDefines.h:28、src/dev_ng/game/Vehicles/wheel.cpp:208 |
CVehicleIntelligence 池 | VEHICLE_POOL_SIZE 个、元素大小取 sizeof(CHeliIntelligence) | src/dev_ng/game/vehicleAi/VehicleIntelligence.cpp:81 |
| 座位 / 入口点 | MAX_VEHICLE_SEATS = 16、MAX_ENTRY_EXIT_POINTS = 16 | src/dev_ng/game/Vehicles/VehicleDefines.h:22-24 |
| 武器 / 炮塔 | 主武器 6、炮台电池 38、炮塔 3 槽 × 4 = 12 | src/dev_ng/game/Vehicles/VehicleDefines.h:69-73 |
| 新车健康 | CREATED_VEHICLE_HEALTH = 1000;起火健康 250;残骸 60 s 后移除 | src/dev_ng/game/Vehicles/VehicleDefines.h:38-40 |
| 空间索引 | CVehicle::InitSystem 用池大小分配 CSpatialArray 存储,并初始化 AI LOD 优先级数组 | src/dev_ng/game/Vehicles/Vehicle.cpp:1105-1123 |
池满时(__BANK)工厂会在 MP 下打印全池清单并断言“请提高 gameconfig.xml 的 PoolSize” src/dev_ng/game/Vehicles/VehicleFactory.cpp:2777-2802。
5. 创建与销毁:CVehicleFactory#
工厂单例在 CGame::CreateFactories/DestroyFactories 中与 ped 工厂、CVehicleIntelligenceFactory 一起创建/销毁 src/dev_ng/game/core/game.cpp:588-605;车辆录像与路网的流式模块也在同一文件的流式模块注册函数中登记 src/dev_ng/game/core/game.cpp:578-581。
5.1 Create 流程#
CVehicleFactory::Create(modelId, ownedBy, popType, pMat, bClone, bCreateAsInactive) 的顺序 src/dev_ng/game/Vehicles/VehicleFactory.cpp:1564-1769:
- 校验模型信息存在且为
MI_TYPE_VEHICLE;若 fragment 缓存无法再分配条目(FRAGCACHEMGR->CanGetNewCacheEntries(1)失败)直接返回NULLsrc/dev_ng/game/Vehicles/VehicleFactory.cpp:1568-1590。 - 原本的“销毁车辆缓存”复用路径被
#if 0关闭src/dev_ng/game/Vehicles/VehicleFactory.cpp:1592-1596,于是总是走CreateVehicle(type);计数CVehiclePopulation::IncrementNumVehiclesCreatedThisCycle()(这会占用本人口周期的生成额度,见第 10 节)src/dev_ng/game/Vehicles/VehicleFactory.cpp:1598-1613。 - 先设置矩阵、再设置
bCreatingAsInactive,最后SetModelId(modelId)——注释强调必须在矩阵之后调用以便正确初始化物理src/dev_ng/game/Vehicles/VehicleFactory.cpp:1615-1635。 - 船拖车计数(
FLAG_SPAWN_BOAT_ON_TRAILER),上限常量为“无船拖车 1 / 有船拖车 2”src/dev_ng/game/Vehicles/VehicleFactory.cpp:1645-1659、src/dev_ng/game/Vehicles/VehicleFactory.h:130-133。 - 网络注册必须在任何其他状态改变之前:
bClone && NetworkInterface::IsGameInProgress()时调用RegisterVehicleToNetwork,失败则立即Destroy返回NULLsrc/dev_ng/game/Vehicles/VehicleFactory.cpp:1668-1685。 AssignVehicleGadgets(5.4);按 PopZone 的脏度范围与颜色、天气修正设置车身脏度,XMAS天气在联机/回放时把脏色设为雪白src/dev_ng/game/Vehicles/VehicleFactory.cpp:1689-1724。- 插入
CVehicle::GetSpatialArray(),记录初始生成位置,更新车身颜色重映射src/dev_ng/game/Vehicles/VehicleFactory.cpp:1726-1736。
注意:Create 不会把车辆加入世界;调用方(人口、脚本、cargen)随后自行 CGameWorld::Add,例如环境车生成路径 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5805-5808。
5.2 CreateVehicle:类型分派与音频实体#
CreateVehicle 用 switch(nVehicleType) rage_new 出具体类,然后按类型挂接音频实体:直升机/旋翼机/飞艇 → audHeliAudioEntity,火车 → audTrainAudioEntity,飞机 → audPlaneAudioEntity,船/潜艇 → audBoatAudioEntity,汽车类 → audCarAudioEntity,自行车 → audBicycleAudioEntity,拖车 → audTrailerAudioEntity,未知类型回退到基类并告警 src/dev_ng/game/Vehicles/VehicleFactory.cpp:2805-2912。导出导航网格碰撞(NAVMESH_EXPORT)时跳过音频初始化 src/dev_ng/game/Vehicles/VehicleFactory.cpp:2862-2866。
5.3 SetModelId → InitPhys → 组件初始化#
CVehicle::SetModelId 是车辆真正“成形”的地方 src/dev_ng/game/Vehicles/Vehicle.cpp:3996-4224:
| 顺序 | 动作 | 证据 |
|---|---|---|
| 1 | CEntity::SetModelId;按 popType 增加模型引用(停放车单独计数) | src/dev_ng/game/Vehicles/Vehicle.cpp:4008-4013 |
| 2 | pHandling = CHandlingDataMgr::GetHandlingData(modelInfo->GetHandlingId());初始化阻力系数与 m_Transmission.SetupTransmission(...)(及可选第二变速箱) | src/dev_ng/game/Vehicles/Vehicle.cpp:4015-4029 |
| 3 | 由 handling mFlags 推导 van/big/bus/low、不爆胎/不断轮、MF_INDESTRUCTIBLE(全部免伤标志)以及 ABS 标准/可选(50% 随机) | src/dev_ng/game/Vehicles/Vehicle.cpp:4035-4068 |
| 4 | 模型标志 FLAG_DISABLE_DEFORMATION 关闭可见损坏;坦克默认免疫火焰 | src/dev_ng/game/Vehicles/Vehicle.cpp:4076-4087 |
| 5 | 随机初始脏度;补建动态组件;InitPhys() | src/dev_ng/game/Vehicles/Vehicle.cpp:4092-4100 |
| 6 | m_SeatManager.Init(numSeats, driverSeat);CreateDrawable、骨架与动画导演 | src/dev_ng/game/Vehicles/Vehicle.cpp:4108-4127 |
| 7 | 组件预留、损坏、灯光开关初始化;警车/执法车辆/警笛标志 | src/dev_ng/game/Vehicles/Vehicle.cpp:4131-4152 |
| 8 | InitWheels();(frag 已缓存时)InitDoors()、InitCompositeBound() | src/dev_ng/game/Vehicles/Vehicle.cpp:4154-4160 |
| 9 | 坦克或带炮塔标志的车型在生成时就创建 CVehicleWeaponMgr | src/dev_ng/game/Vehicles/Vehicle.cpp:4162-4169 |
| 10 | HD 资源状态、请求动态对象导航网格、AddVehicleInstance;低底盘车型默认装上液压改装 | src/dev_ng/game/Vehicles/Vehicle.cpp:4180-4216 |
CVehicle::InitPhys src/dev_ng/game/Vehicles/Vehicle.cpp:4940-5070:
ENABLE_FRAG_OPTIMIZATION下,无环境布料的汽车/拖车在插入时不分配 fragment 缓存条目(bDisableCacheOnInsertion),之后按需GiveFragCacheEntrysrc/dev_ng/game/Vehicles/Vehicle.cpp:4952-4956、src/dev_ng/game/Vehicles/Vehicle.cpp:1768-1778。- 用
fragInstGta(PH_INST_FRAG_VEH, ...)建主实例,设高优先级、手动骨架更新,插入物理层;除非“以非激活状态创建”否则立刻PHSIM->ActivateObjectsrc/dev_ng/game/Vehicles/Vehicle.cpp:4958-4978。 - 若 frag 含
VEH_CHASSIS_DUMMY组则标记bHasDummyBound;否则CAutomobile::InitDummyInst另建一个GTA_BOX_VEHICLE_TYPE的phInstGta作为 dummy 实例src/dev_ng/game/Vehicles/Vehicle.cpp:4995-5004、src/dev_ng/game/Vehicles/Automobile.cpp:4946-4966。
AI 部分由 CVehicle::InitAi 完成:经 CVehicleIntelligenceFactory 创建 CVehicleIntelligence(直升机 → CHeliIntelligence,飞机 → CPlaneIntelligence),并总是压入一个 CTaskVehicleNoDriver(NO_DRIVER_TYPE_INITIALLY_CREATED) 到主任务树 src/dev_ng/game/Vehicles/Vehicle.cpp:29508-29517、src/dev_ng/game/vehicleAi/VehicleIntelligenceFactory.cpp:20-32。
5.4 AssignVehicleGadgets:按骨骼/标志装配#
gadget 不是数据表驱动,而是工厂按 handling 标志与骨骼存在与否硬编码装配 src/dev_ng/game/Vehicles/VehicleFactory.cpp:3030-3252:
| 条件 | gadget | 证据 |
|---|---|---|
MF_HAS_TRACKS | CVehicleTracks | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3039-3044 |
HF_ENABLE_LEAN | CVehicleLeanHelper | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3045-3049 |
FLAG_PARKING_SENSORS | CVehicleGadgetParkingSensor(半径 0.4) | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3051-3056 |
骨骼 VEH_ATTACH | CVehicleTrailerAttachPoint,并禁止成为停放 superdummy | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3058-3065 |
VEH_DIGGER_ARM | CVehicleGadgetDiggerArm(找到后直接 return) | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3067-3082 |
拖车 + VEH_MISC_E | CVehicleGadgetBoatBoom | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3084-3099 |
VEH_TOW_ARM | CVehicleGadgetTowArm | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3102-3119 |
VEH_FORKS+VEH_CARRIAGE / VEH_HANDLER_FRAME_1/2 | 叉车 / Handler 吊架 | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3121-3147 |
VEH_DOOR_HATCH_R/L | CVehicleGadgetBombBay | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3149-3160 |
| 收割机骨骼 / 关节挖掘臂 / EOD 臂 | 对应 gadget | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3162-3219 |
| 扰流板骨骼或下压力修正 >1 | CVehicleGadgetDynamicSpoiler;VEH_FLAP_L/R → 动态襟翼 | src/dev_ng/game/Vehicles/VehicleFactory.cpp:3236-3250 |
多数分支在 ENABLE_FRAG_OPTIMIZATION 下强制 GiveFragCacheEntry(true),因为关节 gadget 需要 articulated body。
5.5 Destroy、销毁缓存与复用池#
CVehicleFactory::Destroy src/dev_ng/game/Vehicles/VehicleFactory.cpp:2679-2772:计入本周期销毁数 → 若在销毁缓存中只清引用 → 网络克隆不允许销毁(断言并返回),否则 NetworkInterface::UnregisterObject → 船拖车计数 → DestroyChildrenVehicles → 从空间数组移除 → 回放通知 → CGameWorld::Remove → CTheScripts::UnregisterEntity → delete。
两套“避免重复创建”的机制状态不同:
- 工厂销毁缓存(
ms_reuseDestroyedVehs,MAX_DESTROYED_VEHS_CACHED = 50):仍保留数据结构与每帧ProcessDestroyedVehsCache注册,但Create的取用与Destroy的放入都被#if 0关闭src/dev_ng/game/Vehicles/VehicleFactory.h:113-126、src/dev_ng/game/Vehicles/VehicleFactory.cpp:1593-1596、src/dev_ng/game/Vehicles/VehicleFactory.cpp:2767-2771、src/dev_ng/game/core/game.cpp:2854。这表明它是遗留路径(推断:缺少关闭原因的注释)。 - 人口复用池(
VEHICLE_REUSE_POOL_SIZE = 15)是实际生效的机制:被移除的环境车若CanVehicleBeReused则冻结、移出寻路服务器并标记SetIsInReusePool(true),之后同型号生成时FindVehicleToReuse/ReuseVehicleFromVehicleReusePool取回;超时、模型被丢弃或 MP 禁用时真正移除src/dev_ng/game/Vehicles/vehiclepopulation.h:1386-1387、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:9065-9075、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:9876-9930、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:10297-10345、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5039、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5630。复用池内的车辆不做 AI 与 LOD 更新src/dev_ng/game/vehicleAi/VehicleAILodManager.cpp:204-208、src/dev_ng/game/Vehicles/Vehicle.cpp:29540-29544。
6. 模型信息与数据驱动元数据#
6.1 CVehicleModelInfo 的车辆字段(vehicles.meta)#
CVehicleModelInfo::InitData(VehicleModelInfoData.psc)是 vehicles.meta 每个条目的结构。车辆领域最重要的字段:
| 字段 | 含义 | 证据 |
|---|---|---|
m_modelName、m_txdName、m_handlingId、m_gameName | 模型/贴图、操控名(字符串→CHandlingDataMgr 索引) | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:196-200 |
m_layout(默认 LAYOUT_STANDARD)、m_POVTuningInfo、m_coverBoundOffsets、m_explosionInfo、m_scenarioLayout | 指向 vehiclelayouts 中的命名对象 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:211-215 |
m_cameraName/m_aimCameraName/m_bonnetCameraName/m_povCameraName | 相机元数据名(相机系统见 21 章) | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:216-220 |
m_AllowPretendOccupants/m_AllowJoyriding/m_AllowSundayDriving | 人口与 AI 行为开关 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:249-251 |
m_lodDistances[VLT_MAX]、m_HDTextureDist | 渲染 LOD 距离 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:266-267 |
m_damageMapScale、m_damageOffsetScale、m_defaultBodyHealth | 形变与车身健康 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:261-262、src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:273 |
m_frequency、m_swankness、m_maxNum、m_flags、m_type、m_vehicleClass、m_wheelType、m_plateType | 人口频率/档次、全局上限、标志位集、VehicleType、类别、轮毂类型、车牌类型 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:281-296 |
m_trailers、m_drivers、m_extraIncludes、m_requiredExtras | 可挂拖车、默认司机、extra 关联 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:298-324 |
m_buoyancySphereOffset/m_buoyancySphereSizeScale | 浮力球(见 09-physics) | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:332-333 |
m_numSeatsOverride | 覆盖布局座位数 | src/dev_ng/game/modelinfo/VehicleModelInfoData.psc:349 |
三个 DLC 可挂载的数据文件类型在 CVehicleModelInfo::InitClass(INIT_AFTER_MAP_LOADED) 注册挂载接口:VEHICLE_METADATA_FILE(vehicles.meta,加载后还会调用 CTrain::TrainConfigsPostLoad 重绑车厢模型)、VEHICLE_VARIATION_FILE(carvariations,AppendVehicleVariations)、CARCOLS_FILE(AppendVehicleColors)src/dev_ng/game/modelinfo/VehicleModelInfo.cpp:176-230、src/dev_ng/game/modelinfo/VehicleModelInfo.cpp:255-262。人口上限与 140 辆基线的比值还会放大各车型的 maxNum 乘数 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:821-825。
6.2 carcols、改装套件与 CVehicleVariationInstance#
全局颜色/改装表 CVehicleModelInfoVarGlobal 以 PSO store 方式从 platform:/data/carcols.<meta> 加载,并注册为 REST 单例 Vehicles/carcols 以便网页工具浏览 src/dev_ng/game/modelinfo/VehicleModelInfo.cpp:541-551。其结构包括车牌、颜色、金属漆、车窗颜色、灯光设置、警笛设置、改装套件 m_Kits、按 VWT_MAX 分类的轮毂、全局变体数据与氙灯颜色 src/dev_ng/game/modelinfo/VehicleModelInfoColors.psc:785-815。
- 改装套件
CVehicleKit:名字(需与 dwd/txd 同名)、ID、类型,可见改装m_visibleMods、仅能被链接带出的m_linkMods、属性改装m_statMods、槽位名覆盖与涂装名src/dev_ng/game/modelinfo/VehicleModelInfoColors.psc:670-692。 - 改装槽位
eVehicleModType分三段:可渲染(VMT_SPOILER…VMT_LIGHTBAR)、属性(VMT_ENGINE/BRAKES/GEARBOX/HORN/SUSPENSION/ARMOUR)、开关(VMT_NITROUS/TURBO/SUBWOOFER/TYRE_SMOKE/HYDRAULICS/XENON_LIGHTS),最后是轮子等杂项src/dev_ng/game/modelinfo/VehicleModelInfoEnums.h:30-114。 - 实例侧:变体实例存放在车辆的绘制处理器中(
GetVehicleDrawHandler().GetVariationInstance()),IsModded()即改装数 > 0src/dev_ng/game/Vehicles/vehicle.h:460-463;SetModIndex触发改装模型流送,流送请求在“Ped and Vehicle assets”更新组中统一处理src/dev_ng/game/modelinfo/VehicleModelInfoVariation.cpp:645、src/dev_ng/game/core/game.cpp:2769-2775。 - 改装对操控的影响:例如“扩展改装”车型用
CCarHandlingData::m_AdvancedData(槽位/索引/值)覆盖涡轮功率,RSG_GEN9下非扩展车型另有分支src/dev_ng/game/Vehicles/Transmission.cpp:143-176。
6.3 vehiclelayouts:CVehicleMetadataMgr#
CVehicleMetadataMgr 是一个包含 20 类命名对象数组的单例:动画速率集、clip set 映射、掩体边界、自行车信息、POV 调校、入口动画变体、额外点、驾驶射击武器组/动画/信息、座位信息与座位动画、入口点信息与入口点动画、爆炸信息、布局信息、场景布局、座位覆盖、车内覆盖、第一人称驾驶射击环视数据 src/dev_ng/game/Vehicles/Metadata/VehicleMetadataManager.psc:4-66。
- 基础文件
commoncrc:/data/ai/vehiclelayouts.meta在Init时PARSER.LoadObject载入;加载后修复座位“换座链接”指针并做数据校验,同时注册 REST 单例Vehicles/Layoutssrc/dev_ng/game/Vehicles/Metadata/VehicleMetadataManager.cpp:34-35、src/dev_ng/game/Vehicles/Metadata/VehicleMetadataManager.cpp:1284-1300。 - DLC 的
VEHICLE_LAYOUTS_FILE通过挂载接口(eDFMI_UnloadLast)调用AppendExtra/RemoveExtra:解析到临时实例后逐数组追加或按名移除src/dev_ng/game/Vehicles/Metadata/VehicleMetadataManager.cpp:1088-1095、src/dev_ng/game/Vehicles/Metadata/VehicleMetadataManager.cpp:1312-1390。 - 布局中的座位/入口点以
external_named指针引用座位信息与动画信息(按名字解析)src/dev_ng/game/Vehicles/Metadata/VehicleLayoutInfo.psc:39-47;布局标志(如NoDriver、StreamAnims、WarpIntoAndOut、MustCloseDoor…)src/dev_ng/game/Vehicles/Metadata/VehicleLayoutInfo.psc:4-37。这些数据主要服务于 ped 进出车/驾驶射击动画(07-ai-tasks 负责)。
6.4 座位、入口点与预留#
CModelSeatInfo::InitFromLayoutInfo(layout, skelData, numSeatsOverride)把布局中的座位/入口点映射到本车型实际存在的骨骼(“非 1:1,以支持缺失的座位/门”),存m_iLayoutSeatIndicies/m_iSeatBoneIndicies与CEntryExitPoint m_aEntryExitPoints[MAX_ENTRY_EXIT_POINTS]src/dev_ng/game/modelinfo/ModelSeatInfo.h:193-288。CEntryExitPoint:一个入口有一个直接可达座位和经换座的间接座位,保存门骨骼与门把手骨骼src/dev_ng/game/modelinfo/ModelSeatInfo.h:150-187;入口评估函数EvaluateSeatAccessFromAllEntryPoints等为 ped 进车任务服务src/dev_ng/game/modelinfo/ModelSeatInfo.h:238-248。- 注意命名:运行时乘员表类名是
CSeatManager(不是CVehicleSeatManager)。它保存m_pSeatOccupiers[MAX_VEHICLE_SEATS]、上一乘员、玩家数与“已排程但尚未创建的乘员”计数(7 位)src/dev_ng/game/modelinfo/ModelSeatInfo.h:290-346;GetDriver()用无符号比较消除分支src/dev_ng/game/modelinfo/ModelSeatInfo.h:352-360。 CComponentReservationManager为每个座位与入口点(MAX_VEHICLE_SEATS + MAX_ENTRY_EXIT_POINTS)维护一个CComponentReservation,让多个 ped 不会同时使用同一扇门/座位src/dev_ng/game/Vehicles/componentReserve.h:20-55。
7. 门、窗与部件:CCarDoor#
CCarDoor 用位标志描述轴(旋转 X/Y/Z 或滑动)与驱动模式:DRIVEN_SHUT/AUTORESET/NORESET/GAS_STRUT/SWINGING/SMOOTH/SPECIAL(后门仅在被炸开或座位劫持时打开),以及闩锁、断裂、articulated、DRIVEN_BY_PHYSICS(车辆关节由动画驱动时仍由物理驱动)等 src/dev_ng/game/Vehicles/door.h:54-96。主要 API:SetTargetDoorOpenRatio、SetSwingingFree、SetShutAndLatched、BreakOff/BreakOffNextUpdate、GetIsIntact src/dev_ng/game/Vehicles/door.h:140-197。
- 门的物理在
CAutomobile::ProcessPhysics里逐门调用CCarDoor::ProcessPhysics:articulated collider 时全部处理,否则只处理带驱动/强制唤醒标志或待断裂的门src/dev_ng/game/Vehicles/Automobile.cpp:3454-3470。 - 门开合比例变化会置
bDoorRatioHasChanged,在延迟任务完成阶段UpdateDoorsForNavigation把打开的门作为动态障碍加入/更新/移出寻路服务器src/dev_ng/game/Vehicles/Vehicle.cpp:25241-25246、src/dev_ng/game/Vehicles/Vehicle.cpp:25054-25080。 - 窗户状态
m_bWindowsRolledDown[]在InitDoors中全部摇上src/dev_ng/game/Vehicles/Vehicle.cpp:4784-4790;车窗玻璃破碎属于 12-vfx 的 vehicleglass。 CBouncingPanel(最多 3 个)由CVehicleDamage持有,模拟被撞松的保险杠/面板src/dev_ng/game/Vehicles/vehicleDamage.h:650、src/dev_ng/game/Vehicles/vehicleDamage.h:757-758。
8. Gadget 与车辆武器#
CVehicleGadget 是一个带多个处理钩子的多态组件:ProcessPrePhysics、ProcessPhysics/2/3、ProcessPreRender、ProcessControl、ProcessPreComputeImpacts、WantsToBeAwake、ChangeLod、BlowUpCarParts,可声明为网络同步并 Serialise src/dev_ng/game/Vehicles/VehicleGadgets.h:178-268。类型枚举前半段(叉车、探照灯、吊绳、挖掘臂、Handler 吊架、电磁铁、弹仓)是网络同步类型,VGT_NUM_NETWORKED_TYPES 之后为本地类型 src/dev_ng/game/Vehicles/VehicleGadgets.h:59-86。
- 车辆在各阶段遍历
m_pVehicleGadgets:ProcessPrePhysicssrc/dev_ng/game/Vehicles/Vehicle.cpp:1788-1791、DoProcessControl_OnDeferredTaskCompletion中ProcessControlsrc/dev_ng/game/Vehicles/Vehicle.cpp:25381-25389、ProcessPhysics2/ProcessPhysics3(吊钩/电磁铁需要第三遍以免与轮子积分任务冲突)src/dev_ng/game/Vehicles/Automobile.cpp:4850-4858、src/dev_ng/game/Vehicles/Automobile.cpp:4927-4930。 CVehicleWeaponMgr:最多 6 个CVehicleWeapon与 12 个CTurret,各自绑定座位索引;Fire、AimTurretsAtWorldPos等src/dev_ng/game/Vehicles/VehicleGadgets.h:859-933。CreateVehicleWeaponMgr仅当 handling 带CVehicleWeaponHandlingData时创建,并把骨骼 1A–1D 组装成同时开火的武器电池src/dev_ng/game/Vehicles/VehicleGadgets.cpp:4900-4935。武器本身(CWeapon/CWeaponInfo)归 ../21-weapons-camera/weapons.md。- 武器处理入口:
DoProcessControl第一步ProcessWeapons()src/dev_ng/game/Vehicles/Vehicle.cpp:25102。
9. 车辆 AI 的挂接点(概述)#
车辆拥有独立于 ped 的任务管理器 CVehicleTaskManager(主树 + 副树)src/dev_ng/game/task/System/TaskManager.h:76-92;任务树/优先级枚举为 VEHICLE_TASK_TREE_PRIMARY/SECONDARY 与 VEHICLE_TASK_PRIORITY_CRASH/PRIMARY、VEHICLE_TASK_SECONDARY_ANIM src/dev_ng/game/Vehicles/VehicleDefines.h:259-279。玩家成为司机时 SetPlayerDriveTask 按 VehicleType 选择 CTaskVehiclePlayerDrive*(拖车没有玩家驾驶任务,飞艇复用直升机任务),并禁止玩家车进入时间片 LOD src/dev_ng/game/Vehicles/Vehicle.cpp:29664-29726。路径、任务族与避障详见 vehicle-ai-and-road-network.md。
10. 车辆人口:CVehiclePopulation#
10.1 每帧 Process#
CVehiclePopulation::Process 在 gameSkeleton 的“Vehicles”人口更新组中注册,紧随其后的是 CVehicleAILodManager::Update 与飞行器避让管理器 src/dev_ng/game/core/game.cpp:2789-2794。其主体 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1245-1645:
- 回放接管时直接返回;玩家未定位(
CStreaming::IsPlayerPositioned)时返回src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1247-1260。 - 计算人口范围缩放;在暂停判断前调用
CVehicle::StartAiUpdate()缓存全部车辆 AI 速度(使新生成车辆能在本帧做一次完整 AI 更新)src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1273-1281。 - 更新事件/调度/指令管理器(
CIncidentManager、CDispatchManager、COrderManager)src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1292-1299。 - 等待上一帧启动的“更新密度”依赖完成(自旋 +
sysIpcYield)src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1303-1321。 - 荒街、禁用节点清理、车距系数;取人口控制点(与 ped 人口共用
CPedPopulation::GetPopulationControlPointInfo)src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1334-1364。 - 复用池维护;移除多余空警车/残骸(MP 与 SP 两套)、空置执法车、联机拥堵与挤占玩家的空车
src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1381-1404。 - 火车与直升机各自的生成/移除逻辑
src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1412-1436。 - 人口周期:按
PopulationFrameRate × CyclesPerFrame把时间折算为周期数;每个周期先RemoveDistantVehs(仅当本周期无销毁),再在三个状态(随机车辆 → 路径上的特殊车辆 → 已排程的 cargen 车辆)间轮转直到有实体被创建,然后CTheCarGenerators::Process;创建/销毁计数每周期递减 1,多出的“溢出”到下一周期,从而限制每个人口周期最多生成一个实体src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1418-1529。 - 扫描“玩家所在道路”的节点;更新钥匙孔(keyhole)生成形状、近裁剪距离,并启动异步收集生成链接作业
src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1582-1598。
10.2 异步作业与生成链接#
生成依赖一个“生成链接”数组 ms_aGenerationLinks[MAX_GENERATION_LINKS](360/PS3 为 1600,其余 4096)src/dev_ng/game/Vehicles/vehiclepopulation.h:900、src/dev_ng/game/game_config.h:51。每个 CVehGenLink 是一对路网节点(m_nodeA/m_nodeB)加上类别、密度(初始/修正后)、车道数、尝试次数、附近车辆数(8:8 定点)、长度/坡度/朝向(定点)、得分与期望车辆差额 src/dev_ng/game/Vehicles/vehiclepopulation.h:402-446。
两个 sysDependency 作业在初始化时绑定回调 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:895-898:
| 作业 | 启动 | 等待 | 内容 |
|---|---|---|---|
VehiclePopulationAsyncUpdate(收集链接) | Process 末尾 StartAsyncUpdateJob,参数含钥匙孔半径、最大节点距离(创建距离 + 20m)、玩家车速、相机朝向等 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:4505-4574 | CGameWorld::Process 在物理更新后调用 UpdateLinksAfterGather 自旋等待 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1198-1230、src/dev_ng/game/scene/world/GameWorld.cpp:2082 | GatherPotentialVehGenLinks 在路网中找出候选链接 src/dev_ng/game/Vehicles/VehiclePopulationJob.cpp:22-50 |
VehiclePopAsyncUpdateDensities(更新密度) | UpdateCurrentVehGenLinks 末尾 StartAsyncUpdateDensitiesJob src/dev_ng/game/Vehicles/vehiclepopulation.cpp:3892-3919、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:4490-4503 | 下一帧 Process 开头 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1303-1321 | 计算链接得分/期望密度(VehPopUpdateDensities_shared.cpp) |
这表明一个链接从被收集到被用来生成车辆至少跨越一帧(推断,基于启动/等待点)。
10.3 生成:从链接到车辆#
TryToGenerateOneRandomVeh 的主要步骤(节选)src/dev_ng/game/Vehicles/vehiclepopulation.cpp:4820-5994:GenerateVehCreationPosFromLinks 选位置 → ChooseModel(考虑警察/通缉、高速、街道关联、越野、禁大车)→ 优先 FindVehicleToReuse → PlaceOnRoadProperly(自行车/汽车各自实现)→ 复用池取回或 CVehicleFactory::Create(..., POPTYPE_RANDOM_AMBIENT, ...) → 添加司机乘客(受实时 ped 名额限制)→ 可选拖车 → CGameWorld::Add → MakeVehicleIntoDummyBasedOnDistance src/dev_ng/game/Vehicles/vehiclepopulation.cpp:4857、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5003、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5458-5470、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5630-5642、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5681、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5731、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5805、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:5898。
MakeVehicleIntoDummyBasedOnDistance 先调用 PostCreationAiUpdate(PreGameWorldAiUpdate → StartAiUpdate → ProcessIntelligence → aiDeferredTasks::RunAll → FinishAiUpdate,让新车立刻有 follow route),再按 CalcDesiredLodForPoint 转 superdummy/dummy src/dev_ng/game/Vehicles/vehiclepopulation.cpp:8652-8692、src/dev_ng/game/Vehicles/Vehicle.cpp:29519-29526。这正是 CAutomobile::DoProcessControl 检查 bHasProcessedAIThisFrame 避免同帧二次 AI 的原因 src/dev_ng/game/Vehicles/Automobile.cpp:1859-1866。
期望的环境车数量 = 上限 × 荒街/通缉(取小)× 室内 × 战斗 × 高速 × 内存预算乘数;MP 不使用荒街与战斗乘数 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:4576-4632。
10.4 回收#
RemoveDistantVehs 每个周期只检查 ceil(环境车上限 / 6) 辆车(静态游标轮转全池),跳过集群中的车(由集群负责)与延迟移除计时未到的车;ShouldRemoveVehicle 给出理由后优先进入复用池,否则 RemoveVeh;不移除时重置或设置新的延迟移除时间 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:9016-9100。ShouldRemoveVehicleInternal 综合距离(cull range 屏内/屏外)、可见性、任务与 popType 等因素 src/dev_ng/game/Vehicles/vehiclepopulation.cpp:9108。
10.5 配置来源#
| 参数 | 来源 | 证据 |
|---|---|---|
| 人口帧率/每帧周期(SP/MP)、环境/停放/低优先停放密度、环境与停放上限、钥匙孔厚度与半径、最大创建距离(屏内/屏外)、cull range、基于密度的移除速率 | gameconfig 的 population 配置(GET_POPULATION_VALUE),多数可被命令行 PARAM 覆盖 | src/dev_ng/game/Vehicles/vehiclepopulation.cpp:786-861 |
| MP 环境车上限、本地缓冲、停放车乘数等 | 云端 Tunables(MP_VEHICLE_UPPER_LIMIT 等键)回落到 gameconfig | src/dev_ng/game/Vehicles/vehiclepopulation.cpp:863-874 |
生成标记球 CVehGenMarkUpSpheres(最多 32 个)与 MP 密度乘数 | VehiclePopulationTuning.psc | src/dev_ng/game/Vehicles/Metadata/VehiclePopulationTuning.psc:5-25 |
10.6 车辆生成点(cargen)与特殊车辆#
CTheCarGenerators 管理地图/脚本放置的生成点(脚本最多 80 个),排程队列最多 32 辆待生成车辆;Process 每周期只处理 ceil(池大小 / (人口帧率 × 每帧周期)) 个生成点,并优先处理 CargenPriorityAreas 区域内的生成点 src/dev_ng/game/Vehicles/cargen.h:25-29、src/dev_ng/game/Vehicles/cargen.cpp:2783-2880、src/dev_ng/game/Vehicles/cargens.psc:5-15。GenerateScheduledVehiclesIfReady 每次弹出一个排程,分阶段等待碰撞探测、放置到路面后才真正生成 src/dev_ng/game/Vehicles/cargen.cpp:2916-2925。警车/急救车/罪犯车由 GenerateOneEmergencyServicesCar、GenerateCopVehicleChasingCriminal 等函数提供给 Dispatch(见 20 章)src/dev_ng/game/Vehicles/vehiclepopulation.cpp:7404、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:7903。
11. 每帧处理顺序#
下表按一帧内的先后列出车辆相关调用(主线程视角):
| 顺序 | 调用 | 车辆侧效果 | 证据 |
|---|---|---|---|
| 1 | CVehicleVariationInstance::ProcessStreamRequests | 处理改装流送请求 | src/dev_ng/game/core/game.cpp:2771 |
| 2 | CVehiclePopulation::Process | StartAiUpdate 缓存速度、移除/生成、启动链接收集作业 | src/dev_ng/game/core/game.cpp:2791、src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1278-1281 |
| 3 | CVehicleAILodManager::Update | 分批(3 帧一轮)决定 real/dummy/superdummy 与时间片 | src/dev_ng/game/core/game.cpp:2792、src/dev_ng/game/vehicleAi/VehicleAILodManager.cpp:388-862 |
| 4 | CScene::Update → CGameWorld::Process | 见下 5–10 | src/dev_ng/game/core/game.cpp:2826、src/dev_ng/game/scene/scene.cpp:809 |
| 5 | CVehicle::PreGameWorldAiUpdate() | 全量更新帧清除“本帧禁止 dummy/superdummy”等标志 | src/dev_ng/game/scene/world/GameWorld.cpp:1950、src/dev_ng/game/Vehicles/Vehicle.cpp:29832-29862 |
| 6 | CScenarioManager::GetVehicleScenarioManager().Update();fwSceneUpdate::Update(SU_UPDATE_VEHICLE) | 对每辆车调用 CVehicle::ProcessControl | src/dev_ng/game/scene/world/GameWorld.cpp:1956-1960 |
| 7 | aiDeferredTasks::RunAll() | 并行执行避障/寻路点/AI 收尾/插值四类延迟任务,再在主线程收尾 | src/dev_ng/game/scene/world/GameWorld.cpp:1965-1966、src/dev_ng/game/vehicleAi/task/DeferredTasks.cpp:375-392 |
| 8 | CVehicle::FinishAiUpdate() | 断言缓存速度未被改变、更新“可驾驶/停放/静止”判定、清除每帧标志 | src/dev_ng/game/scene/world/GameWorld.cpp:1968、src/dev_ng/game/Vehicles/Vehicle.cpp:29864-29931 |
| 9 | CPhysics::Update() | 每个物理时间片 PreSimUpdate 调用 ProcessPhysics/2/3(轮子积分在工作线程) | src/dev_ng/game/scene/world/GameWorld.cpp:2061、src/dev_ng/game/physics/physics.cpp:1374-1510 |
| 10 | CVehiclePopulation::UpdateLinksAfterGather();CVehicleRecordingMgr::RetrieveDataForThisFrame()(非物理同步模式) | 等待链接作业、启动密度作业;推进车辆录像回放 | src/dev_ng/game/scene/world/GameWorld.cpp:2082、src/dev_ng/game/scene/world/GameWorld.cpp:2096-2105 |
| 11 | CVehicleModelInfo::UpdateHDRequests、ThePaths.Update() | HD 资源;路网已用节点更新 | src/dev_ng/game/scene/scene.cpp:813-826 |
| 12 | Game Logic 组:CGps::Update、CJunctions::Update、CTrafficLights::Update | GPS、路口相位、交通灯亮度 | src/dev_ng/game/core/game.cpp:2835-2848 |
| 13 | CGameWorld::ProcessAfterAllMovement | 车辆附着、座椅碰撞边界、寻路服务器重试 | src/dev_ng/game/scene/world/GameWorld.cpp:2208-2240 |
| 14 | Render Update:CVehicle::PreVisUpdate(车内渲染模式)→ CGameWorld::ProcessAfterCameraUpdate(CVehicle::ProcessAfterCameraUpdate → 每车 ProcessPostCamera)→ UpdateLightsOnFlagsForAllVehicles → CVehicleDeformation::TexCacheUpdate | 渲染准备 | src/dev_ng/game/core/game.cpp:2909、src/dev_ng/game/core/game.cpp:2962、src/dev_ng/game/core/game.cpp:2999、src/dev_ng/game/core/game.cpp:3039、src/dev_ng/game/Vehicles/Vehicle.cpp:29948-29960 |
| 15 | 渲染器:CVehicleLightAsyncMgr::SetupDataForAsyncJobs / KickJobForRemainingVehicles / ProcessResults | 车灯与 corona 异步计算 | src/dev_ng/game/renderer/Renderer.cpp:975、src/dev_ng/game/renderer/Renderer.cpp:993、src/dev_ng/game/renderer/Renderer.cpp:1031 |
11.1 单车的 ProcessControl 链#
证据:ProcessControl 计算全量更新与时间片步长并调用 DoProcessControl src/dev_ng/game/Vehicles/Vehicle.cpp:24578-24595;CAutomobile::DoProcessControl 的全量段、AI 段与损坏段 src/dev_ng/game/Vehicles/Automobile.cpp:1799-1866、src/dev_ng/game/Vehicles/Automobile.cpp:1868-2076,末尾调用基类 src/dev_ng/game/Vehicles/Automobile.cpp:2120(摩托与船同理 src/dev_ng/game/Vehicles/Bike.cpp:1011、src/dev_ng/game/Vehicles/boat.cpp:1195);CVehicleIntelligence::Process 排队收尾 src/dev_ng/game/vehicleAi/VehicleIntelligence.cpp:1109-1125;CVehicle::DoProcessControl 排队插值 src/dev_ng/game/Vehicles/Vehicle.cpp:25086-25202;插值任务与主线程收尾 src/dev_ng/game/vehicleAi/task/DeferredTasks.cpp:328-371。
ProcessControl 自身在 DoProcessControl 之后还处理跳跃车、两栖四轮收轮、火箭推进、降落伞、滑翔翼、特殊飞行模式、外伸支腿与竞技场模式等“特殊能力”输入 src/dev_ng/game/Vehicles/Vehicle.cpp:24710-25037;其中“编队领队”拾取与调参是 __BANK 调试功能,但随后的跟随加速逻辑本身不在 __BANK 内(领队指针只能由调试 picker 设置)src/dev_ng/game/Vehicles/Vehicle.cpp:24597-24708。
DoProcessControl_OnDeferredTaskCompletion 只在全量更新时执行:提交河流浮力探测、引擎温度、清除自动复位作弊位、记录最后有司机时间、门导航更新、人行道/空中计时与惊吓事件、震动反馈、溺水灭火、延迟修复、gadget 与武器 ProcessControl、CPhysical::ProcessControl src/dev_ng/game/Vehicles/Vehicle.cpp:25204-25420。
12. 线程模型:延迟任务与作业#
| 机制 | 线程 | 同步方式 | 证据 |
|---|---|---|---|
aiDeferredTasks(4 类任务、最多 512 项、最多 4 个工作线程) | sysTaskManager 工作线程 + 主线程同时消费无锁环 | CreateWorkersAndWait;运行期间打开 fwRegdRef/事件/空间数组等锁并禁止任务删除,切换 PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics | src/dev_ng/game/vehicleAi/task/DeferredTasks.h:13-14、src/dev_ng/game/vehicleAi/task/DeferredTasks.cpp:82-154、src/dev_ng/game/vehicleAi/task/DeferredTasks.cpp:156-197 |
| 轮子积分 | sysDependency(高优先级) | ProcessResult 自旋 sysIpcYield 等待 jobPending 归零 | src/dev_ng/game/Vehicles/wheel.cpp:7893-7958 |
| 人口链接收集 / 密度更新 | sysDependency | 自旋等待 | src/dev_ng/game/Vehicles/vehiclepopulation.cpp:4490-4574 |
| 车灯 | 渲染器发起的 sysDependency:60 个作业 × 每作业 5 辆车(断言容量 ≥ 车辆池) | ProcessResults 等待 | src/dev_ng/game/Vehicles/VehicleLightAsyncMgr.cpp:18-43 |
只有一个任务时不创建工作线程,直接在主线程执行 src/dev_ng/game/vehicleAi/task/DeferredTasks.cpp:96-102;PS3 上 DoProcessControl 与 CVehicleIntelligence::Process 都不走延迟任务(#if !__PS3)src/dev_ng/game/Vehicles/Vehicle.cpp:25184-25201、src/dev_ng/game/vehicleAi/VehicleIntelligence.cpp:1109-1125。
13. 平台与构建差异#
| 条件 | 影响 | 证据 |
|---|---|---|
__XENON || __PS3(XENON_OR_PS3) | 车辆/音频堆 464/488KB(否则 ×10);生成链接 1600(否则 4096);路口最大车辆 64(否则 128)、交通灯信息 85(否则 512)、同时活跃路口 60(否则 128) | src/dev_ng/game/Vehicles/VehicleFactory.cpp:1445-1449、src/dev_ng/game/game_config.h:51-55 |
!__PS3 | 车辆插值与 AI 收尾走延迟多线程任务 | src/dev_ng/game/Vehicles/Vehicle.cpp:25184-25201 |
ENABLE_FRAG_OPTIMIZATION | fragment 缓存按需分配/释放;gadget 装配时强制分配 | src/dev_ng/game/Vehicles/Vehicle.cpp:1768-1778、src/dev_ng/game/Vehicles/Vehicle.cpp:4952-4956 |
ENABLE_DRAFT_VEHICLE | 是否可创建 CDraftVeh | src/dev_ng/game/Vehicles/VehicleFactory.cpp:2840-2844 |
__BANK | 车辆工厂调试 bank、池满时打印清单、编队领队调试、ms_bSpawnTrailers 过滤 | src/dev_ng/game/Vehicles/VehicleFactory.h:139-294、src/dev_ng/game/Vehicles/VehicleFactory.cpp:1578-1584 |
__ASSERT | 车辆存在 10 秒仍未进入物理层会断言;FinishAiUpdate 校验 AI 期间速度未变 | src/dev_ng/game/Vehicles/Vehicle.cpp:25090-25100、src/dev_ng/game/Vehicles/Vehicle.cpp:29753-29782 |
GTA_REPLAY | 回放编辑模式下跳过人口、LOD、AI、物理 | src/dev_ng/game/Vehicles/vehiclepopulation.cpp:1247-1254、src/dev_ng/game/vehicleAi/VehicleAILodManager.cpp:390-393、src/dev_ng/game/Vehicles/Automobile.cpp:3189-3195 |
14. 约束与边界行为#
- 枚举顺序约束:
VehicleType的顺序是类型判断的一部分(第 2 节)。 - 网络注册先于状态变更:
Create中网络注册失败会立即销毁;克隆车辆不可被本地Destroy(断言后返回,不删除)src/dev_ng/game/Vehicles/VehicleFactory.cpp:1668-1685、src/dev_ng/game/Vehicles/VehicleFactory.cpp:2696-2711。 - 玩家车不得成为 dummy:
DoProcessControl开头断言src/dev_ng/game/Vehicles/Vehicle.cpp:25088。 - AI 期间速度只读:
StartAiUpdate缓存速度、FinishAiUpdate校验并失效缓存(NaN),任务若在 AI 阶段改速度会被断言捕获src/dev_ng/game/Vehicles/Vehicle.cpp:29728-29750、src/dev_ng/game/Vehicles/Vehicle.cpp:29907-29931。 - frag 缓存耗尽:
Create直接返回NULL,调用方必须处理失败src/dev_ng/game/Vehicles/VehicleFactory.cpp:1586-1590。 - 人口周期限额:工厂每次创建都会增加“本周期创建数”,因此脚本/调度生成的车辆会挤占环境车生成机会(推断,依据第 5.1 与 10.1 的计数点)。
- 复用池与 AI:复用池中的车辆被
ShouldDoFullUpdate、ProcessIntelligence、延迟任务校验等多处显式跳过src/dev_ng/game/vehicleAi/task/DeferredTasks.h:44-49。
15. 未决问题与证据不足#
- 未在源码树中找到
gameconfig.xml、handling.meta、vehicles.meta、vehiclelayouts.meta、carcols.meta等数据文件,故车辆池实际大小与各人口参数的默认值无法给出(仅能给出读取点)。 CVehicle::PreRender/PreRender2(灯光、轮子渲染矩阵、扰流板等)只在本页按调用位置提及,未逐项展开;渲染侧细节归 11 章。ShouldRemoveVehicleInternal的完整判定树(约 460 行)与ChooseModel的模型选择权重未逐行分析。- 工厂销毁缓存被禁用的原因无注释说明。