CD Code Digest Hub /GTA V · v2

Ped 架构:CPed、工厂、标志、组件与 PedGroup

72 分钟阅读 16 个章节 3 张图

本页解释 GTAV 中“行人(ped)”这一实体类型在 C++ 层的骨架:CPed 持有哪些子对象、如何被 CPedFactory 创建与销毁、两套标志系统(config flags / reset flags)如何在帧内被重置、一个 ped 每帧经历的处理顺序、它与 CPedModelInfo 的数据交接、外观组件(变体/道具)的数据层、玩家专有的 CPlayerInfo,以及编组系统 CPedGroup。最后简述位于 RAGE suite 的 pedsafezone(它实际服务于车辆形变)。

ped 是整个游戏最复杂的实体:Ped.cpp 单文件约 33,000 行,ped.h 约 3,700 行。理解本页后再阅读 AI、人口、运动与渲染子页会更顺畅。

背景与相关页面#

相关源码文件#

文件作用
src/dev_ng/game/Peds/ped.h、Ped.cppCPed 类定义与实现(构造、每帧处理、物理、渲染前处理、变体入口)
src/dev_ng/game/Peds/pedDefines.h池大小宏、状态枚举(死亡/逮捕/布娃娃)、任务树与优先级枚举
src/dev_ng/game/Peds/PedFlags.h、PedFlags.cpp、PedFlagsMeta.pscCPedConfigFlags/CPedResetFlags 与 parser 生成的位集枚举
src/dev_ng/game/Peds/PedFactory.h、PedFactory.cpp创建/销毁 ped 与玩家 ped、池初始化、(已停用的)销毁缓存
src/dev_ng/game/Peds/PedType.hePedType
src/dev_ng/game/Peds/PedComponentSetInfo.*、PedCapsule.*、HealthConfig.*、TaskData.*、PedIKSettings.*、NavCapabilities.*由模型信息哈希引用的数据驱动配置
src/dev_ng/game/Peds/rendering/PedVariationDS.hePedVarComp、CPedVariationData、CPedHeadBlendData、CPedVariationInfoCollection
src/dev_ng/game/Peds/rendering/PedProps.h道具锚点与 CPedPropData
src/dev_ng/game/Peds/PlayerInfo.h、PlayerInfo.cpp、PlayerInfo.psc玩家附加数据 CPlayerInfo
src/dev_ng/game/PedGroup/PedGroup.h、PedGroup.cpp、formation.h、formation.cppped 编组、成员与编队
src/dev_ng/rage/suite/src/pedsafezone/pedsafezone.h、.cpp车辆座舱保护区 CPedSafeZone
src/dev_ng/game/renderer/Entities/PedDrawHandler.hCPedDrawHandler(变体数据实际的存放处,渲染侧)

1. 对象模型总览#

CPed 派生自 CPhysical,并通过 FW_REGISTER_CLASS_POOL(CPed) 使用独立对象池;其构造函数是 protected 且只对 CPedFactory 开放(注释强调“必须使用 CPedFactory”)src/dev_ng/game/Peds/ped.h:214-238。一个 ped 的大部分行为由挂在它身上的“组件”对象实现:

图中证据:智能、玩家信息、库存、武器管理器、面部、电话、头盔、动画/布娃娃实例指针 src/dev_ng/game/Peds/ped.h:2866-2878;m_MotionData、m_IkManager src/dev_ng/game/Peds/ped.h:2736、ped.h:2754;m_pedAiLod ped.h:2882;CPedDrawHandler 访问器 ped.h:1924-1925;绘制句柄中的变体/道具数据与流送渲染 gfx src/dev_ng/game/renderer/Entities/PedDrawHandler.h:20-44。

要点:

  • 外观数据(CPedVariationData、CPedPropData)不在 CPed 内,而在其 CPedDrawHandler 中;CPed::SetVariation 等接口只是转发(见第 8 节)。
  • CPedIntelligence 也有独立池(见 ped-intelligence-and-population.md),由 CPedIntelligenceFactory::Create 分配 src/dev_ng/game/Peds/Ped.cpp:1621-1622。
  • “控制者”由 CControlledByInfo 两个位描述:是否网络控制、是否玩家;由此派生本地 AI / 网络 AI / 本地玩家 / 网络玩家四种判定 src/dev_ng/game/Peds/ped.h:159-190。构造时写入 m_isControlledByNetwork / m_isControlledByPlayer Ped.cpp:1327-1329。

2. CPed 的内存布局与关键成员#

2.1 池与“人口缓存”过滤#

  • CPed 与 CPedIntelligence 池均用 CONFIGURED_FROM_FILE 实例化(容量取自 gameconfig.xml),COMMERCE_CONTAINER 构建使用无 flex 溢出的变体 src/dev_ng/game/Peds/PedFactory.cpp:74-80。
  • MAXNOOFPEDS 不再是编译期常量,而是 CPoolHelpers::GetPedPoolSize(),注释说明“曾经是 90,现在可经 gameconfig.xml 设置” src/dev_ng/game/Peds/pedDefines.h:10-16;值由 system.cpp 从配置管理器读取 "Peds" 池大小后写入 src/dev_ng/game/system/system.cpp:965,GetPedPoolSize() 在未设置时 TrapZ PedFactory.cpp:1112-1134。
  • fwPool<CPed>::GetSlot 有模板特化:处于“人口缓存”的 ped(GetIsInPopulationCache())对池遍历不可见 src/dev_ng/game/Peds/ped.h:3604-3620。注意:这是 m_bIsInPopulationCache 位,与人口复用池的 CPED_CONFIG_FLAG_PedIsInReusePool 是两套不同机制(见第 6.4 节和人口页)。

2.2 “不要乱动”的位域块#

ped.h 中有一段注释为“THE FOLLOW CODE IS OPTIMIZED! DON'T FUCK WITH IT!”的紧凑位域区,把调试 AI 时最常查看的状态压在一起:控制者、人口计数归属、m_bRemoveAsSoonAsPossible、m_bIsInPopulationCache 等布尔位,以及 ePedLodState:4、eRagdollState:4、eArrestState:3、eDeathState:3、ePedType:7、期望/当前布娃娃池各 4 位、约束类型 3 位,之后是伤害集 ID 等 u8 src/dev_ng/game/Peds/ped.h:2756-2864。注释里的 “65/64 .. nice .. off by one...” 说明该块已超出 64 位 1 位。

2.3 关键成员分组#

分组代表成员说明 / 证据
运动与地面m_vDesiredVelocity、m_vecGroundPos、m_vecGroundNormal、m_vGroundVelocity*、m_MotionData期望速度、地面探测结果、站在移动物体上的参考速度 ped.h:2698-2736
音频/脚步m_PedFootStepHelper、m_PedAudioEntity、m_pSpeechAudioEntityped.h:2738-2740、ped.h:2874
人口计时m_delayedRemovalResetTimeMs/AmountMs、m_delayedConversion*、m_InFovTime、m_creationTime删除/转换延迟计时器,供人口系统判断 ped.h:2919-2925
数据集指针(不拥有)m_pCapsuleInfo、m_pComponentClothInfo、m_pComponentSetInfo、m_pMotionTaskDataSet、m_pDefaultTaskDataSet、m_pIKSettings、m_pTaskData由模型信息哈希解析得到 ped.h:2950-2973
每 ped 物理原型m_pPhysicsArchetype注释:每个 ped 持有自己原型的实例,以便自由修改 bounds ped.h:2967-2968
掩体m_pCoverPoint、m_pDesiredCoverPointped.h:2975-2976
约束m_RotationalConstraintX/Y、m_CylindricalConstraint防止胶囊体俯仰/翻滚 ped.h:3018-3025
特殊能力m_specialAbilities[PSAS_MAX]、m_selectedAbilitySlotPSAS_MAX = 2 ped.h:3027-3028、PlayerInfo.h:87-93
布料/布娃娃m_ClothCollision、m_CClothController[PV_MAX_COMP]、m_pRagdollInst(为 SPU 代码取地址而 public)ped.h:3061-3065
标志m_PedConfigFlags、m_PedResetFlags、缓存的 m_CachedSceneUpdateFlagsped.h:3067-3071
生命值/护甲m_fMaxEndurance、m_nArmour、受伤阈值 m_f*HealthThresholdped.h:3132-3147
地面物理体m_pGroundPhysical、m_pLastValidGroundPhysical、m_pTrainRidingOnped.h:3175-3183
空间索引m_spatialArrayNode(静态 ms_spatialArray)ped.h:3219-3220、ped.h:3548
编组m_iPedGroupIndex注释:由 CPedGroups::Process() 每帧开始时设置 ped.h:3284
动作模式m_pMovementModes、m_MovementModeClipSets、四个 fwClipSetRequestHelper个性化移动模式与其片段集流送 ped.h:3315-3345
调试(!__FINAL)m_debugPedName[16]、m_damageRecords(最多 10 条)ped.h:3379-3391

平台/构建差异:

  • RSG_PC 下 m_nArmour 包装为 sysLinkedData<float, ReportArmorMismatch>,其他平台为裸 float ped.h:3137-3141。这属于反篡改范畴,本 Wiki 只记录其存在,细节见 ../23-debug-security/security-overview.md。
  • 马匹相关成员(m_pMyMount、m_pSeatManager、m_pHorseComponent)只在 ENABLE_HORSE 下存在 ped.h:3153-3155、ped.h:3238-3243;GTAV 构建中该宏为 0(见 ped-subsystems.md)。
  • 喷气背包 m_pJetpack 在 ENABLE_JETPACK 下存在 ped.h:3157-3159;ENABLE_JETPACK 仅在 !__FINAL && !__XENON 为 1 src/dev_ng/game/task/Movement/JetpackObject.h:9-13。
  • ENABLE_DRUNK 定义为 0 ped.h:81。

2.4 关键状态枚举#

枚举值证据
ePedLodStatePLS_HD_NA / NONE / REQUESTED / AVAILABLE / REMOVING(HD 资源状态)ped.h:136-145
eDeathStateAlive / Dying / DeadpedDefines.h:78-86
eArrestStateNone / ArrestedpedDefines.h:69-76
eRagdollStateANIM_LOCKED / ANIM_PRESETUP / ANIM / ANIM_DRIVEN / PHYS_ACTIVATE / PHYSpedDefines.h:88-98
ePedTypePLAYER_0..2、NETWORK_PLAYER、CIVMALE/CIVFEMALE、COP、11 种帮派、DEALER/MEDIC/FIRE/...、SWAT、ANIMAL、ARMYsrc/dev_ng/game/Peds/PedType.h:11-46

IsDead() 不是单看死亡状态:要求 GetIsDeadOrDying() 且生命值 ≤ 0;注释说明后者在 MP 中仍然需要 ped.h:1402-1418。

3. 标志系统:Config Flags 与 Reset Flags#

两个标志类都基于 parser 生成的位集枚举(PedFlagsMeta.psc 中 <enumdef ... generate="bitset">):

类语义规模证据
CPedConfigFlags“相对静态”的能力/选项/特殊设置,持续有效直到被改写ePedConfigFlags 462 个枚举值;另有 4 个小位域(不可被撞下车模式、腿部 IK 模式、手势模式、组内乘客座位索引)与 fClimbRateOverridesrc/dev_ng/game/Peds/PedFlags.h:15-60、PedFlagsMeta.psc:5-468
CPedResetFlags每帧自动复位的“通知”标志,主要由任务设置以触发别处代码ePedResetFlags 460 个枚举值;另有若干倒计数器与每帧复位的浮点修正量PedFlags.h:63-183、PedFlagsMeta.psc:470-993

(数量为对 .psc 中 <enumval> 行的计数。)

3.1 Reset flags 的分阶段复位#

CPedResetFlags 并非“每帧一次清零”,而是持有 7 个静态掩码,在帧内不同时间点按掩码清除不同子集(IntersectNegate):

复位函数头文件注释给出的时机实际调用点
Reset每帧CPed::ProcessControl_ResetVariables Ped.cpp:30674
ResetPreAIPostInventoryAI 前、库存后ProcessControl_WeaponsAndAccessoriesPreIntelligence Ped.cpp:31183
ResetPreTask任务前、事件处理后CPedIntelligence::Process_Tasks PedIntelligence.cpp:3136-3139
ResetPrePhysics物理前、ProcessControl 后CPed::ProcessPhysics Ped.cpp:19970
ResetPostPhysics物理后、脚本前CPedIntelligence::ProcessPostPhysics PedIntelligence.cpp:1434-1438
ResetPostMovement / ResetPostPreRender脚本后 / PreRender 后注释 PedFlags.cpp:1090-1113
ResetPostCamera相机后CPed::ProcessPostCamera Ped.cpp:31472

函数声明与注释见 PedFlags.h:71-80,掩码在 StaticInit 中填充 PedFlags.cpp:433-470,StaticInit 由 CPed::InitSystem 调用 Ped.cpp:2225。

Reset() 的细节 PedFlags.cpp:981-1058:

  1. 先根据 DontActivateRagdollFromAnyPedImpact(reset 或 config 版本)同步布娃娃实例的 FLAG_NM_NO_PLAYER_PED_ACTIVATE。
  2. 保存需要“跨帧保留”的少数位(ResetMoveGroupAfterRagdoll、KnockedToTheFloorByPlayer 在仍处于布娃娃时保留;InternalWalkingRndPlayer 转写为 IsWalkingRoundPlayer)。
  3. 掩码清除主位集。
  4. 倒计数类字段(m_nDontAcceptIKLookAts、m_nHasJustLeftVehicle、m_nIsInCover、m_nSetEntityZFromGround、m_nKnockedByDoor)各减 1——头文件注释解释:为避免设置与复位的先后顺序问题,这类标志设为 2/3 后逐帧递减 PedFlags.h:132-137。
  5. 浮点修正量(动画旋转、移动动画速率、座位间距)恢复默认,并始终置位 SyncDesiredHeadingToCurrentHeading。

ResetPostPhysics 还会给本地玩家重新打开 BF_CanDoDrivebys 战斗行为位,ResetPostPreRender 会清除 SpawnedThisFrameByAmbientPopulation PedFlags.cpp:1075-1113。

维护提示:新增 reset flag 时,除了在 .psc 中加枚举,还要决定它属于哪个掩码;不加入任何掩码的位不会被自动清除(推断:依据 StaticInit 显式逐位登记的写法)。

4. 每帧处理顺序#

4.1 帧级时序(与 ped 相关的部分)#

以下按 CGame 更新组注册顺序和 CGameWorld::Process 内部顺序整理:

顺序调用对 ped 的作用证据
1CPedVariationStream::ProcessStreamRequests、CPedPropsMgr::ProcessStreamRequests、MeshBlendManager::Update(组 “Ped and Vehicle assets”)完成变体/道具流送请求,推进头部混合src/dev_ng/game/Core/game.cpp:2769-2775
2CPedPopulation::Process、CPedAILodManager::Update(组 “World and Population / Peds”)生成/回收;重新分配 AI/动画 LODgame.cpp:2803-2807
3CScene::Update → CPopCycle::Update → CGameWorld::Process人口周期更新后进入世界处理game.cpp:2826、src/dev_ng/game/scene/scene.cpp:803-809
3aCGameWorld::StartAnimationUpdate启动异步动画更新(ped 的 StartAnimUpdate 受动画时间切片控制)src/dev_ng/game/scene/world/GameWorld.cpp:1858-1861
3bCPedGeometryAnalyser::Process收集上一帧异步 ped↔ped LOS 结果GameWorld.cpp:1873-1874
3cCPed::StartAiUpdate更新脚步辅助类数据、刷新所有 ped 在 CSpatialArray 中的位置GameWorld.cpp:1949、Ped.cpp:6294-6308
3dfwSceneUpdate::Update(SU_UPDATE) → SceneUpdateUpdateCb → CPed::ProcessControl(克隆 ped 只跑网络对象的 ProcessControl)主 AI/逻辑更新;被冻结的实体走 ProcessFrozenGameWorld.cpp:1954、GameWorld.cpp:2832-2871
3eCRelationshipManager::UpdateRelationshipGroups、EndAnimationUpdate、删除列表处理、CPedGroups::Process关系组清理、等待动画完成、编组维护GameWorld.cpp:1995-2036
3fCPhysics::Update → SceneUpdatePreSimPhysicsUpdateCb → CPed::ProcessPhysics每个物理时间片调用;可返回 POSTPONEGameWorld.cpp:2060-2062、GameWorld.cpp:2912-2950
3gCPedScriptedTaskRecord::Process汇总脚本任务记录GameWorld.cpp:2125-2127
4CPedFactory::ProcessDestroyedPedsCache、CTheScripts::Process、CGameWorld::ProcessAfterAllMovement缓存老化(见 6.4)、脚本、移动后处理game.cpp:2853、game.cpp:2862、game.cpp:2876
5camManager::Update → CGameWorld::ProcessPedsEarlyAfterCameraUpdate → CPed::ProcessPostCamera相机后 IK 请求、可选“即时”AI/动画更新game.cpp:2900-2923、GameWorld.cpp:2423-2457
6PreRender 阶段:CPed::PreRender / PostPreRenderLOD 状态、IK、伤害优先级、着色器参数Ped.cpp:23301-23420

ProcessPedsEarlyAfterCameraUpdate 在多线程动画更新前把动画导演设为“全局锁定”,并把启动了即时动画更新的 ped 原始指针压入数组,注释明确依赖“此间不会删除该 ped”的假设 GameWorld.cpp:2429-2453。整体帧时序的权威描述见 ../01-runtime/game-skeleton-and-main-loop.md 与 ../05-world/world-and-entities.md。

4.2 CPed::ProcessControl 内部顺序#

Ped.cpp:18670-18842:

图中证据:入口与早退 Ped.cpp:18674-18687;时间切片判定 Ped.cpp:18693-18698;AnimStateUpdate 等待并交换缓冲 Ped.cpp:30520-30547;fullUpdate 分支 Ped.cpp:18704-18727;其余步骤 Ped.cpp:18738-18783;末尾调用基类 Ped.cpp:18840-18841。

细节:

  • 时间切片:fullUpdate 来自 AI LOD 管理器;TUNE_BOOL(TimesliceProcessControl, true) 关闭时所有非智能部分恢复每帧完整更新,而智能部分始终使用原始 fullUpdateForIntelligence Ped.cpp:18693-18698。
  • 跳过 AI:CPED_RESET_FLAG_SkipAiUpdateProcessControl 置位时不调用 ProcessControl_Intelligence Ped.cpp:18750-18755。
  • ProcessControl_Intelligence 依次:UpdateMovementMode() → CPedMotionData::ProcessPreTaskTrees() → CPedIntelligence::Process() → ProcessPostTaskTrees() → 重置可查询接口中的脚本任务信息 → 玩家 reset flags 的 ResetPostAI Ped.cpp:31225-31246。
  • 低 LOD 早退:ProcessControl_Animation 和 ProcessControl_Graphics 在 AL_LodEventScanning 置位(低事件扫描 LOD)时直接返回;ProcessControl_Audio 在该 LOD 下不播放脚步 Ped.cpp:30584-30597、Ped.cpp:30779-30781。
  • ProcessControl_Physics 处理“被玩家踢/被门撞”前后帧标志、布娃娃状态、NM 足部碰撞历史、玻璃碰撞测试,并在满足条件时请求运动学(kinematic)物理模式 Ped.cpp:31003-31076。
  • ProcessFrozen(被冻结时替代 ProcessControl)只推进事件计时、移除无效事件、停语音、处理“特殊网络离开” Ped.cpp:18558-18576;濒死但尚未死亡的 ped 不允许被冻结,以便播放死亡动画 Ped.cpp:18578-18588。
  • “特殊网络离开”:MP 中 ped 在 m_specialNetworkLeaveTimeRemaining 内按网络工具给出的曲线淡出 alpha,计时结束后从载具移除、隐藏并标记网络对象删除(或 FlagToDestroyWhenNextProcessed)Ped.cpp:30697-30770。

4.3 物理阶段:ProcessPrePhysics / ProcessPhysics#

  • ProcessPrePhysics:若 ped 使用低 LOD 物理(AL_LodPhysics)或 OverridePhysics,直接调用 SimulatePhysicsForLowLod(),并把自己从 SU_PRESIM_PHYSICS_UPDATE 场景更新中移除(这样不会进入 ProcessPhysics),只做少量 CPhysical::ProcessPrePhysics 的必要工作后返回;否则确保加回该场景位再走基类 Ped.cpp:19827-19895。ProcessPhysics 开头断言低 LOD ped 不会到达这里 Ped.cpp:19899-19904。
  • 单步优化:当 IsPedProcessPhysicsSingleStepActive 为真(且未被 reset flag 强制每步),非首时间片只做站立弹簧力(或复用首片缓存的 m_CurrentSpringForceZ)然后返回;首片把时间步乘以片数一次性处理 Ped.cpp:19917-19956。
  • 推迟:站在其它物理体上时返回 PHYSICS_POSTPONE,若地面物理体是网络克隆则返回 PHYSICS_POSTPONE2(需要等克隆的网络混合器更新)Ped.cpp:19972-19985;世界侧的推迟队列长度受 PreSimControl::MAX_POSTPONED_ENTITIES 限制 GameWorld.cpp:2922-2950。
  • 之后依次:地面移动、参考系速度、ApplyMovementRequests、克隆的网络混合器 ProcessPrePhysics、ProcessBounds(无变化时 AI ped 每 4 帧才处理一次)、任务树 ProcessPhysics、睡眠判定、站立弹簧、浮力、期望速度 Ped.cpp:20038-20177。

物理/布娃娃的深入内容见 ../09-physics/game-physics-and-ragdoll.md;运动侧见 ped-motion-and-rendering.md。

5. 系统级初始化#

入口内容证据
CGame::CreateFactoriesCPedFactory::CreateFactory()、CPedIntelligenceFactory::CreateFactory()game.cpp:588-594
CPed::InitSystem(由 scene.cpp 在物理初始化后调用)伤害管理器、纹身装饰管理器、头像管理器、ped 自定义着色器类、CPedGroups::Init、任务序列、CPedResetFlags::StaticInit、CAi::Init、变体流送/道具系统、按池大小(向上对齐到 4)分配 CSpatialArrayPed.cpp:2215-2247、scene.cpp:659
CPed::Init(INIT_SESSION)(gameSkeleton 注册)打包/流送变体初始化、动机数据、道具管理器、CRelationshipManager::Init、精度修正数据Ped.cpp:2275-2309、game.cpp:2326

6. CPedFactory:创建、销毁与玩家 ped#

6.1 工厂单例与池#

CPedFactory 是单例(ms_pInstance)src/dev_ng/game/Peds/PedFactory.h:39-47。构造函数负责初始化多个 ped 相关池:CPed(MAXNOOFPEDS)、CPedTargetting、CTaskInfo(在 flex 内存中,非旧主机为 400 KB 堆)、CChatHelper、TaskSequenceInfo;析构时对称关闭 PedFactory.cpp:117-173。注释给出 TaskInfo 堆的估算:“268 peds × 8 task infos × 平均 90 字节 ≈ 192 kB” PedFactory.cpp:129-135。

6.2 CreatePed(非玩家)#

证据:PedFactory.cpp:219-350、SetUpAsCivilianPed PedFactory.cpp:891-916、SetUpAsCopPed PedFactory.cpp:930-970、SetPedModelMatAndMovement PedFactory.cpp:1031-1048、ConcreteCreatePed PedFactory.cpp:1062-1109。

要点与边界:

  • 玩家必须用 CreatePlayerPed,对玩家控制信息调用 CreatePed 会断言 PedFactory.cpp:222-225。注意:无论传入什么控制信息,CreatePed 实际都用本地 AI 控制信息构造 ped PedFactory.cpp:237。
  • 池满时 ConcreteCreatePed 返回 NULL,断言提示“在 common/data/gameconfig.xml 增大 PoolSize”,除非 bFailSilentlyIfOutOfPeds PedFactory.cpp:1072-1084。成功创建会累加人口系统的“本周期已创建”计数 PedFactory.cpp:1103-1106。
  • 注释强调“网络注册必须在对 ped 做任何进一步状态修改之前完成” PedFactory.cpp:295-308。
  • 随机生成(非脚本)的平民会被设为可被激怒,且非执法者禁止使用低优先级座位;随机警察获得 LOADOUT_COP、射速修正 0.3、可逮捕,并改为漫游默认任务 PedFactory.cpp:903-912、PedFactory.cpp:943-966、PedFactory.cpp:84。
  • !__FINAL 下全局 gbAllowPedGeneration 可完全禁止生成 PedFactory.cpp:250-255。

其它创建入口:CreatePedFromSource、ClonePed、ClonePedToTarget(克隆外观/伤害/头部混合)PedFactory.h:49-52。

6.3 构造函数与 Init 做了什么#

构造函数 Ped.cpp:1211-1377:

  1. 设置实体类型 SetTypePed()、可见性类型 VIS_ENTITY_PED,重置删除/转换延迟计时。
  2. 从 CPedModelInfo 读取默认 ePedType、默认格斗风格、默认徒手武器。
  3. 用模型信息中的哈希查表绑定:胶囊信息(CPedCapsuleInfoManager)、组件布料(可为空)、组件集(CPedComponentSetManager)、运动任务数据集、默认任务数据集。缺失时断言。
  4. 调用 CPed::Init(false, pModelInfo)。

CPed::Init Ped.cpp:1379-1700 依据 CPedComponentSetInfo 按需创建可选组件(库存与武器管理器、面部、布娃娃约束、马匹组件、电话、头盔),然后创建 CPedIntelligence 并安装 4 棵任务树及默认任务:

树类型槽位数初始任务
PED_TASK_TREE_PRIMARYCTaskTreePedPED_TASK_PRIORITY_MAX(5)默认槽 CTaskDoNothing(-1)
PED_TASK_TREE_MOVEMENTCTaskTreeMovementPED_TASK_MOVEMENT_MAX(3)默认槽 CTaskMoveStandStill
PED_TASK_TREE_SECONDARYCTaskTreePED_TASK_SECONDARY_MAX(1)—
PED_TASK_TREE_MOTIONCTaskTreeMotionPED_TASK_MOTION_MAX(1)由 SetModelId → StartPrimaryMotionTask 安装

证据:Ped.cpp:1644-1655、枚举 pedDefines.h:264-307。任务树语义见 ../07-ai-tasks/task-system.md。

Init(bool reinit, ...) 的 reinit 参数用于复用路径:所有 rage_new 都有 !reinit 守卫,复用时断言物理实例已存在 Ped.cpp:1629-1640。

6.4 销毁#

  • DestroyPed:若有网络对象先 NetworkInterface::UnregisterObject(克隆脚本对象被销毁时断言),再 DestroyPedInternal PedFactory.cpp:1140-1175。
  • DestroyPedInternal 顺序:关闭布料碰撞 → 人口计数(销毁计数 + 从人口计数移除)→ 标记 PedBeingDeleted → 取消脚本注册 → 从本地控制的编组移除 → 清 CreatedByFactory → CGameWorld::Remove → 标记“正在删除的 ped”、让手持道具一并销毁、FlushImmediately 终止所有任务 → delete PedFactory.cpp:545-616。
  • 析构函数断言必须在工厂销毁函数中执行(__DEV)、必须已离开场景图(否则自行 Remove 并警告)、不在空间数组中,并在复用池计数上回退模型引用 Ped.cpp:1883-1914。

“销毁 ped 缓存”实际已停用。 CPedFactory 仍保留 MAX_DESTROYED_PEDS_CACHED = 30、10 秒过期的缓存数组和 ProcessDestroyedPedsCache(每帧注册调用)PedFactory.h:107-116、PedFactory.cpp:95-98、PedFactory.cpp:1312-1354,但所有“放入缓存/从缓存取出/池满时驱逐”的调用点都包在 #if 0 中:CreatePed 的 CreatePedFromDestroyedCache PedFactory.cpp:239-243、DestroyPedInternal 的 AddDestroyedPedToCache PedFactory.cpp:580-586、DestroyPed 的缓存检查 PedFactory.cpp:1145-1152、ConcreteCreatePed 的 EjectOnePedFromCache PedFactory.cpp:1074-1076。在源码中检索,AddDestroyedPedToCache 再无其他调用者,因此推断该缓存永远为空。实际生效的 ped 复用机制是人口系统的 复用池(CPedPopulation::AddPedToReusePool,见 ped-intelligence-and-population.md),它复用了工厂的 InitCachedPed src/dev_ng/game/Peds/pedpopulation.cpp:3946。

6.5 玩家 ped#

CreatePlayerPed PedFactory.cpp:713-780:

  • 若未传入 CPlayerInfo:网络开启时取本地网络玩家的 CPlayerInfo,否则 rage_new CPlayerInfo;只有在未传入时才注册网络对象(传入意味着网络对象已存在)。
  • SetUpAsPlayerPed 互相挂接 CPed::SetPlayerInfo / CPlayerInfo::SetPlayerPed,在可行时建立以玩家为首领的编组(MP 中需要先有网络对象和物理玩家索引),并设置“可在载具中嘲讽”和全自动射击模式 PedFactory.cpp:981-1019。
  • 本地玩家写入 m_pLocalPlayer;覆盖已有本地玩家只允许在“更换玩家模型”时发生,旧玩家关闭第一人称标志;MP 中注册为 CLONEALWAYS | SCRIPTOBJECT PedFactory.cpp:749-770。

DestroyPlayerPed 会停用特殊能力、FlushImmediately、注销网络对象;若网络玩家仍拥有该 CPlayerInfo 则不删除它(由网络玩家负责),__BANK 下还检查是否仍有其他 ped 引用该 CPlayerInfo PedFactory.cpp:789-877。玩家模型切换由 CGameWorld 驱动(调用 CreatePlayerPed/DestroyPlayerPed/SetLocalPlayer)GameWorld.cpp:4537、GameWorld.cpp:4668-4672、GameWorld.cpp:5016-5021。

7. 与 CPedModelInfo 的数据交接#

ped 的数据驱动属性来自其模型信息中的一组哈希。构造函数与 SetModelId 把这些哈希解析成指向全局管理器中条目的指针或运行时状态:

模型信息字段(getter)解析为管理器 / 数据文件证据
GetPedCapsuleHashm_pCapsuleInfoCPedCapsuleInfoManager(PED_BOUNDS_FILE)Ped.cpp:1351-1353、src/dev_ng/game/scene/FileLoader.cpp:1480-1499
GetPedComponentClothHashm_pComponentClothInfo(可空)CPedComponentClothManagerPed.cpp:1355-1356
GetPedComponentSetHashm_pComponentSetInfoCPedComponentSetManagerPed.cpp:1358-1360
GetMotionTaskDataSetHash / GetDefaultTaskDataSetHashm_pMotionTaskDataSet / m_pDefaultTaskDataSetCMotionTaskDataManager / CDefaultTaskDataManagerPed.cpp:1362-1368、FileLoader.cpp:1595-1629
GetTaskDataHashm_pTaskData,并把若干 CTaskFlags 映射为逃跑行为/配置标志CTaskDataInfoManagerPed.cpp:2791-2817
GetCombatInfoHashCCombatBehaviour::InitFromCombatDataCCombatInfoMgr(combatbehaviour.meta)Ped.cpp:2911-2912
个性数据 GetPersonalitySettings携带金钱、默认武器装备—Ped.cpp:2922-2951
GetHasHDFiles / GetIsHDTxdCapablem_pedLodState 初值、HAS_HD_TEX_DIST 基础标志—Ped.cpp:2953-2965
GetIsStreamedGfx选择变体实现(流送 vs 打包)—Ped.cpp:2863-2866、Ped.cpp:8988-8995

SetModelId(fwModelId, ...) 是外观/物理的真正装配点 Ped.cpp:2837-2990:CEntity::SetModelId → InitPhys() 物理实例 → CreateDrawable() → CreateSkeleton() → IK 管理器初始化 → CreateAnimDirector(流送 ped 不传自定义着色器效果)→ 面部片段集 → SwitchToAnimated → StartPrimaryMotionTask → 决策器、关系组、动机、导航能力、感知 → 战斗信息 → 打包 ped 的着色器设置 → InitHealth → 默认装备 → HD 状态 → 可选 CPedVfx → AddPedInstance(模型信息维护实例列表)。注释指出“模型未加载”会断言 Ped.cpp:2841-2843。

CPedComponentSetInfo 决定可选组件是否存在,字段(全部带 parser 默认值):m_HasFacial、m_HasRagdollConstraints、m_HasInventory、m_HasVfx、m_HasSpeech、m_HasPhone、m_HasHelmet、m_HasShockingEventReponse、m_HasReins、m_IsRidable src/dev_ng/game/Peds/PedComponentSetInfo.psc:4-16。例如动物模型可借此不带库存/电话/头盔组件(推断:依据这些开关的语义;具体哪些模型关闭由数据决定,源码中不可见)。

8. 外观组件与道具(数据层)#

本节只描述数据结构;流送、头部混合与渲染的实现见 ped-motion-and-rendering.md。

8.1 组件槽位#

ePedVarComp 定义 12 个组件槽:HEAD, BERD(面具/胡须), HAIR, UPPR, LOWR, HAND, FEET, TEEF, ACCS, TASK, DECL, JBIB src/dev_ng/game/Peds/rendering/PedVariationDS.h:55-72。组件标志 ePedCompFlags 共 21 位(BULKY、JOB、SUNNY、WET、COLD、NOT_IN_CAR、ARMOURED、各类头盔、HIDE_IN_FIRST_PERSON 等),用于随机选装与游戏逻辑(如雨天警察换雨衣)PedVariationDS.h:28-53、PedVariationDS.h:203。容量上限:每组件纹理变体 MAX_TEX_VARS = 32,模型变体 MAX_DRAWBL_VARS 在 __BANK 为 32、否则 16,玩家为 128,调色板 16 PedVariationDS.h:169-181。

8.2 CPedVariationData#

每个 ped 一份(位于 CPedDrawHandler::m_varData)。核心是按槽位的并行数组:drawable 哈希、cloth 变体数据、drawable 索引(u32)、替代 drawable 索引、纹理索引、调色板索引;另有 LOD 淡入值、“高 LOD 组件”位集与若干渲染属性位(含 alpha/decal/cutout、着色器是否已设置、首个变体是否已设置)PedVariationDS.h:248-413。GetIsHighLOD() 即 m_LodFadeValue > 129,GetIsSuperLOD() 即该值为 0 PedVariationDS.h:281-282。

8.3 变体信息集合与 DLC 叠加#

模型级的变体元数据保存在 CPedVariationInfoCollection 中,它持有多个 CPedVariationInfo(基础 + DLC 包)。全局 drawable/道具索引 = 局部索引 + 排在该 DLC 之前的各 info 的最大数量之和,这就是 DLC 服装扩展的索引方案 PedVariationDS.h:742-800。

8.4 CPed 上的变体入口#

接口行为证据
SetVariation(slot, drawbl, alt, tex, palette, streamFlags, force)先经 CGen9ExclusiveAssets::CanCreatePedDrawable 过滤;流送模型走 CPedVariationStream::SetVariation,否则 CPedVariationPack::SetVariation;改 BERD 时重置头盔被击次数Ped.cpp:8968-9002
SetVarRandom(setFlags, clearFlags, ...)交给模型的 CPedVariationInfoCollection::SetVarRandom,可带环境变体的组件限制Ped.cpp:8936-8966
头部混合 SetHeadBlendData 等仅流送 ped 可用,索引必须 < 256Ped.cpp:9330-9351
预加载 SetPreloadData / HasPreloadDataFinished脚本预加载服装ped.h:1966-1971

8.5 道具#

道具挂在 13 个锚点之一(ANCHOR_HEAD/EYES/EARS/MOUTH/LEFT_HAND/.../PH_R_HAND),每 ped 最多 MAX_PROPS_PER_PED = 6 个道具,每道具最多 8 种纹理;CPedPropData 以 SinglePropData{anchor, propModelID, propTexID} 数组记录 src/dev_ng/game/Peds/rendering/PedProps.h:41-70、PedProps.h:160-200。

9. 数据驱动配置一览#

数据加载者证据
PED_BOUNDS_FILE(胶囊/bounds,xml)CFileLoader::LoadCapsuleFile → CPedCapsuleInfoManager::InitFileLoader.cpp:1480-1499、src/dev_ng/game/Peds/PedCapsule.cpp:44-72
PED_HEALTH_FILE(pedhealth.meta)CHealthConfigInfoManager::InitFileLoader.cpp:1501-1517
组件集 / 组件布料(meta)CPedComponentSetManager::Init / CPedComponentClothManager::Init(支持 Append/Remove Extra,用于 DLC)src/dev_ng/game/Peds/PedComponentSetInfo.cpp:39-108、PedComponentSetInfo.cpp:192-207
任务数据 / 导航能力 / IK 设置(meta)CTaskDataInfoManager / CPedNavCapabilityInfoManager / CPedIKSettingsInfoManagersrc/dev_ng/game/Peds/TaskData.cpp:33-77、NavCapabilities.cpp:19-39、PedIKSettings.cpp:82
健康配置字段默认生命/护甲/耐力、疲劳/受伤/濒死/受创阈值、狗扑倒阈值、子弹致扭动阈值、无敌src/dev_ng/game/Peds/HealthConfig.h:18-61
ped 标志PedFlagsMeta.psc(代码生成枚举,非运行时数据)PedFlagsMeta.psc:1-6

10. CPlayerInfo:玩家专有数据#

CPlayerInfo : fwExtensibleBase,只挂在玩家 ped 上(IsAPlayerPed() 即 m_pPlayerInfo != NULL)ped.h:1915-1919、src/dev_ng/game/Peds/PlayerInfo.h:315。主要内容:

方面说明证据
玩家状态机PLAYERSTATE_PLAYING / HASDIED / HASBEENARRESTED / FAILEDMISSION / LEFTGAME / RESPAWN / IN_MP_CUTSCENEPlayerInfo.h:323-333、PlayerInfo.h:821-822
身份rlGamerInfo m_GamerInfo(presence/peer/id/name)PlayerInfo.h:528
通缉CWanted m_Wanted,在 ProcessControl 中每帧 UpdatePlayerInfo.h:1596、PlayerInfo.cpp:2153;详见 ../20-gameplay/wanted-crime-dispatch.md
玩家瞄准CPlayerPedTargeting m_targetingPlayerInfo.h:1483;见 ped-subsystems.md
玩家 reset flagsCPlayerResetFlags(ResetPreAI/ResetPostAI/ResetPostPhysics)PlayerInfo.h:117-160、PlayerInfo.cpp:2077
街机模式信息CPlayerArcadeInformation(C&C 队伍/角色)PlayerInfo.h:1249-1250、PlayerInfo.h:1776
可调参数CPlayerInfo::Tunables(冲刺控制、玩家属性、敌人冲锋、战斗逗留、耐力管理等)src/dev_ng/game/Peds/PlayerInfo.psc:4-102

CPlayerInfo::ProcessControl 由 CPed::ProcessControl_Data 调用(同时调用 ProcessLightEffects、ProcessFPSState)Ped.cpp:30557-30569。对网络玩家只做简单处理后立即返回;对本地玩家断言其人口类型为 POPTYPE_PERMANENT,处理第一人称切换计数、水炮计时、站在警车上报案(每 64 帧检查一次)、通缉更新、武器烹饪计时等 PlayerInfo.cpp:2067-2160。SetupPlayerGroup 在玩家专属编组槽上以 POPTYPE_PERMANENT 激活编组并设本玩家为首领 PlayerInfo.cpp:7008-7018。

11. PedGroup:编组、成员与编队#

11.1 全局编组表#

CPedGroups::ms_groups 是固定数组,容量 = 玩家组 MAX_NUM_PHYSICAL_PLAYERS(32) + 脚本组 16 + 环境组 16 = 64 src/dev_ng/game/PedGroup/PedGroup.h:294-336、src/dev_ng/rage/framework/src/fwnet/nettypes.h:85。AddGroup 按人口类型选择区段 src/dev_ng/game/PedGroup/PedGroup.cpp:1480-1543:

人口类型槽位区段
POPTYPE_PERMANENT(玩家)[0, 32)
POPTYPE_MISSION / REPLAY / TOOL / CACHE[32, 48)
POPTYPE_RANDOM_*[48, 64)

约束:任务(mission)编组必须带脚本句柄,非任务编组不得带脚本句柄;MP 中仍在仲裁(通知其他玩家已清空)的槽位不能复用 PedGroup.cpp:1485-1531。ped 端以 m_iPedGroupIndex(PEDGROUP_INDEX_NONE = 255)记录所在组 PedGroup.h:166。

11.2 成员关系#

CPedGroupMembership 有 MAX_NUM_MEMBERS = CPedFormation::MAX_FORMATION_SIZE(8) 个槽位,最后一个槽(索引 7)固定是首领 PedGroup.h:78-86、src/dev_ng/game/PedGroup/formation.h:132-135。每个成员槽同时保存 RegdPed 和网络 ObjectId(MP 中 ped 可能尚未在本机存在)PedGroup.h:46-69。最大分离距离 ms_fMaxSeparation 与玩家组版本均为 30 m PedGroup.cpp:79-80。

11.3 每帧维护#

CPedGroups::Process(在 CGameWorld::Process 中、物理更新前调用):__ASSERT 下校验每个成员的 m_iPedGroupIndex;对每个活动组调用 CPedGroup::Process;非永久且本地控制的组在成员数为 0 时移除;MP 中首领变更的组要搬到新槽位(组在网络上以首领网络 ID 标识),搬不动就删除 PedGroup.cpp:1707-1778。

CPedGroupMembership::Process(仅本地控制组)PedGroup.cpp:607-673:

  1. 移除死亡/濒死成员(玩家首领除外),以及“已分配但实体已不存在”的槽。
  2. 无首领则 AppointNewLeader:选第一个非克隆成员移到首领槽;调度生成的 SWAT 首领可能不加默认任务 PedGroup.cpp:675-708。
  3. 移除离首领超过最大分离距离的跟随者(NeverLeavesGroup 除外);MP 中玩家首领受伤时移除所有跟随者。
  4. MP 下 AssignNetworkPedIds。

11.4 编队#

CPedFormationTypes 枚举 9 种编队:LOOSE、两种包围、横排、箭头、V 形、纵列跟随、单人、成对;对脚本只暴露 5 种 SCRIPT_FORMATION_* formation.h:52-79。每种编队是 CPedFormation 的子类,持有 CPedPositioning m_PedPositions[8];编队类型与间距会被序列化进网络同步 formation.h:129-247。FORMATION_FOLLOW_IN_LINE 额外保存首领位置/朝向历史以实现“沿路径跟随” formation.h:366-398。跟随任务本身属于 ../07-ai-tasks/task-catalogue.md。

12. suite/src/pedsafezone:车辆座舱保护区#

尽管名为“ped safe zone”,rage::CPedSafeZone 并不处理行人;它计算车辆模型空间中一个“乘员安全包围盒”,用于让车辆形变在座舱区域衰减,从而保护乘客区域 src/dev_ng/rage/suite/src/pedsafezone/pedsafezone.h:36-87。

  • 启用条件:ADD_PED_SAFE_ZONE_TO_VEHICLES 只在 Orbis/Durango/PC 为 1,Xenon/PS3 为 0;注释说明安全区写入顶点色绿通道由 ragebuilder 完成,旧主机来不及改数据 pedsafezone.h:23-28。
  • 计算:查找最多 18 个座位骨骼(seat_dside_*、seat_pside_*)的默认平移,取 AABB 并按侧面 0.29、脚部 0.93、下方 0.40、头部 0.86、背部 0.64 米扩展;外层“不安全区”再外扩最大损伤范围;若启用车轮保护,还根据 10 个车轮骨骼到安全区的最远距离放大缓冲 src/dev_ng/rage/suite/src/pedsafezone/pedsafezone.cpp:15-227。
  • 查询:GetPedSafeAreaMultiplier 在安全区内返回最小值 0.05,在不安全区外返回 1,中间按到安全区距离线性插值 pedsafezone.cpp:238-258。
  • 使用者:游戏侧 CVehicleDamage 持有 m_PedSafeZone src/dev_ng/game/Vehicles/vehicleDamage.h:430-442;工具侧 ragebuilder 在构建车辆资源时用同一算法写入每顶点损伤缩放 src/dev_ng/rage/framework/tools/src/cli/ragebuilder/ragebuilder.cpp:2572、ragebuilder.cpp:2758。头文件注释警告:修改相关常量需重建所有 vehicles.rpf 数据 pedsafezone.h:15-21。车辆损伤系统的权威说明见 ../08-vehicles/vehicle-physics-handling-damage.md。

13. 约束与边界行为#

  • 必须走工厂:CPed 构造受保护;__DEV 下析构函数断言“只能在工厂销毁函数中删除,否则会给网络 ped 带来问题” Ped.cpp:1887-1889;CPed::Init 断言当前位于工厂内部 Ped.cpp:1385。
  • 延迟删除:世界更新期间不能直接删除实体;SceneUpdateUpdateCb 把带 REMOVE_FROM_WORLD 的实体放进每帧上限 MAX_ENTITIES_TO_DELETE_PER_FRAME 的列表,统一在 AI 更新后移除 GameWorld.cpp:2819-2830、GameWorld.cpp:2007-2021。
  • 销毁时的重入保护:DestroyPedInternal 在 FlushImmediately 前后设置/校验 CGameWorld::SetPedBeingDeleted,并断言 flush 没有把 ped 重新插回场景图 PedFactory.cpp:588-605。
  • 默认任务不可为空:__ASSERT 下世界更新会对没有默认任务的 ped 断言 GameWorld.cpp:2852-2859。
  • 克隆 ped:网络克隆不执行 CPed::ProcessControl,只执行其网络对象的 ProcessControl GameWorld.cpp:2861-2871。
  • HD 状态机已失效:CPed::Update_HD_Models 的整个函数体包在 #if 0 中 Ped.cpp:6141-6210,且源码中再无其他把 m_pedLodState 设为 PLS_HD_REQUESTED/AVAILABLE 的代码。因此 ProcessControl 中按 PLS_HD_AVAILABLE 统计的 HIGH_LOD_PEDS 计数推断恒为 0 Ped.cpp:18729-18736。HD 纹理改由 HAS_HD_TEX_DIST 走通用纹理 LOD 路径(推断,依据 SetModelId 中设置该标志 Ped.cpp:2962-2965;纹理 LOD 实现不在本页范围)。

14. 未决问题 / 证据不足#

  • ProcessPrePhysics、ProcessPostMovement、PreRender 的世界侧调用者没有逐一追到具体回调函数行号;本页只给出 ped 侧实现,世界侧细节以 ../05-world/world-and-entities.md 为准。
  • gameconfig.xml 中 Peds 池的实际数值不在源码树中(数据文件),本页无法给出。CTaskInfo 堆注释提及的 “268 peds” 与 MAX_NUM_TASK_INFOS 中 PC/Durango/Orbis 的 268 一致 src/dev_ng/game/Peds/QueriableInterface.h:34,这表明新世代池大小约为 268,但仍需数据确认。
  • PlayerPed.cpp / Population.cpp 存在于目录中但不在构建工程里(见 ped-subsystems.md 的盘点),其中描述的旧接口(如 CPlayerPed、CPopulation)不代表运行时行为。