CD Code Digest Hub /GTA V · v2

世界与实体:CGameWorld、CEntity 类层次、场景实体与对象

83 分钟阅读 11 个章节 5 张图

本页解释 GTAV 游戏层如何表示“世界中的东西”:CEntity 及其派生类如何在 RAGE 框架的 fwEntity 之上扩展、实体如何被放入/移出世界(外景空间结构或内景房间)、每帧的实体处理循环(CGameWorld::Process 及其 scene-update 回调)、实体池与类型标识,以及 game/Objects(CObject/CDummyObject/CDoor/对象人口)和 scene/entities(CCompEntity 等)这些具体实体族。

背景与相关页面#

相关源码文件#

文件内容
game/scene/EntityTypes.heEntityType、eEntityOwnedBy 枚举与掩码
game/scene/Entity.h/.cppCEntity(游戏实体基类)、CEntityFlags、可见性模块、池查找表
game/scene/DynamicEntity.h/.cppCDynamicEntity(可动/可动画实体、portal tracker、scene update 注册)
game/scene/Physical.h/.cppCPhysical(物理实体,CPed/CVehicle/CObject 的基类)
game/scene/Building.*、AnimatedBuilding.*静态地图实体 CBuilding、可动画地图实体 CAnimatedBuilding
game/scene/EntityBatch.*实例列表实体 CEntityBatch、草实例 CGrassBatch
game/scene/entities/compEntity.*复合实体(Rayfire)CCompEntity
game/scene/world/GameWorld.*CGameWorld:Add/Remove、每帧 Process、清理区域、玩家查找
game/scene/world/GameScan.*游戏侧可见性扫描驱动(与场景流式的接口见 game-streaming-and-scene-streamer.md)
game/scene/scene.cppCGtaSceneInterface::InitPools、CScene::Init/Update
game/scene/EntityId.h实体选择 ID(类型 + 池槽位编码)
game/Objects/*CObject、CDummyObject、CDoor/CDoorSystem、CObjectPopulation、程序化对象
game/scene/decorator/decoratorInterface.*游戏侧 fwDecoratorInterface 实现

1. 实体类型与类层次#

1.1 eEntityType:类型标识即类层次索引#

CEntity 的“类型”是一个枚举值,注释直接给出了每个值对应的类继承链,这是全游戏 GetIsTypeXxx()、搜索掩码、池查找的共同键 src/dev_ng/game/scene/EntityTypes.h:14-34:

eEntityType类继承链(源码注释)
ENTITY_TYPE_BUILDINGCBuildingCEntity
ENTITY_TYPE_ANIMATED_BUILDINGCAnimatedBuildingCDynamicEntity : CEntity
ENTITY_TYPE_VEHICLECVehicleCPhysical : CDynamicEntity : CEntity
ENTITY_TYPE_PEDCPedCPhysical : CDynamicEntity : CEntity
ENTITY_TYPE_OBJECTCObjectCPhysical : CDynamicEntity : CEntity
ENTITY_TYPE_DUMMY_OBJECTCDummyObjectCEntity
ENTITY_TYPE_PORTALCPortalInstCBuilding : CEntity
ENTITY_TYPE_MLOCInteriorInstCBuilding : CEntity
ENTITY_TYPE_NOTINPOOLS——仅用于触发音频
ENTITY_TYPE_PARTICLESYSTEMCPtFxSortedEntityCEntity
ENTITY_TYPE_LIGHTCLightEntityCEntity
ENTITY_TYPE_COMPOSITECCompEntityCEntity
ENTITY_TYPE_INSTANCE_LISTCEntityBatchCEntityBatchBase : CEntity
ENTITY_TYPE_GRASS_INSTANCE_LISTCGrassBatchCEntityBatchBase : CEntity
ENTITY_TYPE_VEHICLEGLASSCOMPONENTCVehicleGlassComponentEntityCEntity

每个类型都有对应的位掩码 ENTITY_TYPE_MASK_*,供世界表示(world rep)和搜索按类型过滤 src/dev_ng/game/scene/EntityTypes.h:59-77。eEntityOwnedBy 记录“谁创建/拥有了这个实体”(RANDOM、SCRIPT、IPL、POPULATION、INTERIOR、COMPENTITY、LAYOUT_MGR 等 22 种)src/dev_ng/game/scene/EntityTypes.h:79-103,存于 CEntity::m_ownedBy 的 5 位字段中 src/dev_ng/game/scene/Entity.h:671。地图实例化的实体在 InitEntityFromDefinition 末尾统一被设为 ENTITY_OWNEDBY_IPL src/dev_ng/game/scene/Entity.cpp:634。

证据:CEntity : fwEntity src/dev_ng/game/scene/Entity.h:182;CBuilding : CEntity src/dev_ng/game/scene/Building.h:16;CDynamicEntity : CEntity src/dev_ng/game/scene/DynamicEntity.h:103;CAnimatedBuilding : CDynamicEntity src/dev_ng/game/scene/AnimatedBuilding.h:29;CPhysical : CDynamicEntity src/dev_ng/game/scene/Physical.h:192;CObject : CPhysical src/dev_ng/game/Objects/object.h:166;CDoor : CObject src/dev_ng/game/Objects/Door.h:41;CDummyObject : CEntity src/dev_ng/game/Objects/DummyObject.h:18;CCompEntity : CEntity src/dev_ng/game/scene/entities/compEntity.h:61;CLightEntity : CEntity src/dev_ng/game/renderer/Lights/LightEntity.h:51;CEntityBatchBase/CEntityBatch/CGrassBatch src/dev_ng/game/scene/EntityBatch.h:43-112。CPortalInst/CInteriorInst 的继承见 interiors-and-portals.md。

1.2 CEntity:游戏实体基类#

CEntity 在 fwEntity 上增加的状态刻意保持紧凑:

  • CEntityFlags m_nFlags 恰好 32 个位域,注释要求“除非别无选择不要再加标志”,并以 CompileTimeAssert(sizeof(CEntityFlags) == sizeof(u32)) 锁死 src/dev_ng/game/scene/Entity.h:83-127。其中包含 bInMloRoom(实体位于内景房间)、bIsFrag、bIsProcObject、bHasSpawnPoints、bNeverDummy、bFoundInPvs 等世界管理关键位。
  • 第二组位域打包了环境光遮蔽缩放(m_naturalAmbientScale/m_artificialAmbientScale 各 8 位)、m_iplIndex(16 位,记录来自哪个 IMAP 槽位)、m_ownedBy(5 位)、m_tintIndex(6 位,断言 <64)、m_baseDeferredType 等 src/dev_ng/game/scene/Entity.h:653-687、src/dev_ng/game/scene/Entity.h:744。
  • 尺寸受编译期断言约束:CENTITY_INTENDED_SIZE = 16 + FWENTITY_INTENDED_SIZE,在 RAGE_INSTANCED_TECH/ENABLE_MATRIX_MEMBER 组合下分别断言为 208/136/200/128 字节 src/dev_ng/game/scene/Entity.h:35、src/dev_ng/game/scene/Entity.h:711-724。
  • 可见性由多个模块共同决定:eIsVisibleModule(DEBUG、CAMERA、SCRIPT、CUTSCENE、NETWORK、DUMMY_CONVERSION 等 17 个)各占一位,存在扩展 CIsVisibleExtension 中,全部置位才可见 src/dev_ng/game/scene/Entity.h:129-172。这让脚本、过场、网络等系统各自隐藏实体而互不覆盖。
  • PPUVIRTUAL 宏在 SPU 编译下展开为空(去掉 virtual),使同一头文件可被 PS3 SPU 作业包含 src/dev_ng/game/scene/Entity.h:57-63。

从地图定义初始化。 CEntity::InitEntityFromDefinition 先调用 fwEntity 版本,再把 CEntityDef 的加载标志翻译成渲染相位可见性掩码:FLAG_DONT_RENDER_IN_*/FLAG_ONLY_RENDER_IN_* 组合被映射到 VIS_PHASE_MASK_SHADOWS/REFLECTIONS/WATER_REFLECTION/MIRROR_REFLECTION,并带有若干针对特定 bug 的硬编码组合与一个按模型名的修补(prop_generator_03b)src/dev_ng/game/scene/Entity.cpp:440-576。随后推导阴影代理/水反射代理标志、灯光阴影标志、色调与 AO 缩放 src/dev_ng/game/scene/Entity.cpp:578-635。在 __ASSERT 构建中,互相矛盾的 only/don't 标志会触发断言(这是资产校验路径)src/dev_ng/game/scene/Entity.cpp:499-551。

绘制句柄。 CEntity::CreateDrawable 在实体不可见或已有 draw handler 时直接返回;有 frag 类型时取 fragType 的公共 drawable 并置 bIsFrag;成功后 AddRef 模型,并对 SLOD3/SLOD4 等“容器 LOD”以及(定时、SLOD1、重要自发光)实体设置 STRFLAG_DONTDELETE 使其常驻 src/dev_ng/game/scene/Entity.cpp:945-999。所谓“重要自发光”是 m_iplIndex 等于 downtown/vinewood 两个 IMAP 槽位,这两个槽位由 CFileLoader::CacheLoadEnd 硬编码查找 dt1_emissive、ch2_03 得到 src/dev_ng/game/scene/Entity.h:400、src/dev_ng/game/scene/FileLoader.cpp:2160-2176。

默认虚函数。 基类的 RequiresProcessControl() 返回 false,ProcessControl() 直接断言——只有动态实体会进入每帧处理列表 src/dev_ng/game/scene/Entity.cpp:693-706。GetMainSceneUpdateFlag() 默认返回 SU_UPDATE,GetStartAnimSceneUpdateFlag() 默认返回 SU_START_ANIM_UPDATE_PRE_PHYSICS src/dev_ng/game/scene/Entity.cpp:4253-4261;CVehicle 覆盖为 SU_UPDATE_VEHICLE src/dev_ng/game/Vehicles/Vehicle.cpp:15953-15956。

1.3 CDynamicEntity:能动、能动画、能跨内外景#

CDynamicEntity 增加了三类能力 src/dev_ng/game/scene/DynamicEntity.h:103-295:

  1. scene update 成员资格:AddToSceneUpdate/RemoveFromSceneUpdate,并通过 fwSceneUpdate::IsInSceneUpdate 查询 src/dev_ng/game/scene/DynamicEntity.h:112-118。
  2. 动画:CreateAnimDirector、StartAnimUpdate/EndAnimUpdate、骨骼更新 src/dev_ng/game/scene/DynamicEntity.h:155-190。
  3. 内景追踪:内嵌 CPortalTracker m_portalTracker,注释称“用于追踪在内景间移动的对象” src/dev_ng/game/scene/DynamicEntity.h:192-194、src/dev_ng/game/scene/DynamicEntity.h:283;以及“跨门户(straddling portal)”容器索引 src/dev_ng/game/scene/DynamicEntity.h:253-259。

CDynamicEntityFlags 是 16 位位域:3 位 nStatus、bFrozenByInterior/bFrozen、bIsOutOfMap、bIsStraddlingPortal、4 位 nPopType 与 4 位 nPopTypePrev;NUM_POPTYPES <= 16 由编译期断言保证 src/dev_ng/game/scene/DynamicEntity.h:75-98。ePopType(RANDOM_PERMANENT/PARKED/PATROL/SCENARIO/AMBIENT、PERMANENT(玩家)、MISSION、REPLAY、CACHE、TOOL)决定实体是否属于脚本(IsPopTypeMission = MISSION 或 PERMANENT)src/dev_ng/game/scene/DynamicEntity.h:47-73。

可见性时间戳。 m_FrameCountLastVisible 与静态 sm_FrameCountForVisibility 比较即可判断“本帧是否在某视口可见”,无需每帧清零;sm_CanUseCachedVisibilityThisFrame 在镜头切换时置 false,因为相机更新到 PreRender 之间该缓存滞后一帧 src/dev_ng/game/scene/DynamicEntity.h:196-198、src/dev_ng/game/scene/DynamicEntity.h:274-293。CGameWorld::StartPostCameraAnimationUpdate 根据相机帧的 cut 标志设置它 src/dev_ng/game/scene/world/GameWorld.cpp:1301-1311。

注册哪些 scene update 位。 CDynamicEntity::AddToSceneUpdate 必须在主线程调用;它总是加 SU_RESET_VISIBILITY 与 SU_START_ANIM_UPDATE_POST_CAMERA,未走动画队列时加主更新位,有 anim director 时加动画位;ped 额外加 SU_ADD_SHOCKING_EVENTS | SU_UPDATE_PERCEPTION | SU_UPDATE_PAUSED(低 LOD 物理的 ped 不加 SU_PRESIM_PHYSICS_UPDATE);火车加 SU_TRAIN;CPhysical 加物理前/后更新位 src/dev_ng/game/scene/DynamicEntity.cpp:595-652。

1.4 CPhysical 与三大派生#

CPhysical 引入物理相关的大量标志(伤害免疫、水中、运动学物理、可被货运直升机吊起等)src/dev_ng/game/scene/Physical.h:205-260,以及 ProcessControl/ProcessPrePhysics/ProcessPostPhysics 与分时片 ProcessPhysics/2/3(返回 ePhysicsResult:PHYSICS_POSTPONE、PHYSICS_DONE、PHYSICS_NEED_SECOND_PASS 等)src/dev_ng/game/scene/Physical.h:68-73、src/dev_ng/game/scene/Physical.h:565-571。附着(attachment)、伤害、物理时间片的细节属于 ../09-physics/game-physics-and-ragdoll.md。CPed/CVehicle 见各自章节;CObject 见第 5 节。

1.5 静态与特殊地图实体#

类关键行为证据
CBuilding静态地图实体,只覆盖 InitEntityFromDefinition/CreateDrawable;池容量来自配置(注释“was 40000”)src/dev_ng/game/scene/Building.h:14-30
CAnimatedBuilding非物理但会动画的地图实体;RequiresProcessControl() 恒为 true;FORCEANIM_DIST_MAX=400、FORCEANIM_SIZE_MIN=5 控制强制动画src/dev_ng/game/scene/AnimatedBuilding.h:26-63
CDummyObject地图道具的“休眠”形态,不参与物理;可 CreateObject() 升级为 CObject 并 Enable/Disable 自身src/dev_ng/game/Objects/DummyObject.h:18-48
CEntityBatch由 fwPropInstanceListDef 构造的“道具实例列表”,单实体代表一批同模型实例,自定义 draw handler 与实例缓冲;记录 HD map data 索引src/dev_ng/game/scene/EntityBatch.h:73-110
CGrassBatch由 fwGrassInstanceListDef 构造的草实例批次;持有设备资源(GRASS_BATCH_CS_CULLING 时为 UAV 缓冲,否则为顶点缓冲),按质量设置开关src/dev_ng/game/scene/EntityBatch.h:112-170
CInstanceListAssetLoaderKEEP_INSTANCELIST_ASSETS_RESIDENT=1 时,把实例列表用到的道具模型常驻src/dev_ng/game/scene/EntityBatch.h:17、src/dev_ng/game/scene/EntityBatch.h:172-188
CLightEntity灯光作为实体进入空间结构,持有父实体引用 m_parentEntity;渲染侧细节见 ../11-rendering/deferred-lighting-and-shadows.mdsrc/dev_ng/game/renderer/Lights/LightEntity.h:51-138

草批次的渲染(GPU 剔除、LOD)属于 ../11-rendering/environment-rendering.md。

1.6 地图实体工厂:archetype 决定实体类#

IMAP 中的每个实体定义由框架 fwMapData::ConstructEntity 通过 archetype->CreateEntity() 实例化,然后 SetModelId + InitEntityFromDefinition src/dev_ng/rage/framework/src/fwscene/mapdata/mapdata.cpp:130-140;实例列表/草走 CreateEntityFromDefinition 重载 src/dev_ng/rage/framework/src/fwscene/mapdata/mapdata.cpp:151-208。游戏侧 CBaseModelInfo::CreateEntity 是真正的工厂 src/dev_ng/game/modelinfo/BaseModelInfo.cpp:255-295:

证据:分支见 src/dev_ng/game/modelinfo/BaseModelInfo.cpp:259-284;批次见 src/dev_ng/game/modelinfo/BaseModelInfo.cpp:287-295;MLO 见 src/dev_ng/game/modelinfo/MloModelInfo.cpp:226-233。

关键推论:地图里的道具(“object”类型 archetype)永远先以 CDummyObject 形式存在,只有对象人口管理在靠近时才把它升级为 CObject(第 5.2 节)。

2. 实体池与类型标识#

2.1 池初始化#

场景池在 CGtaSceneInterface::InitPools 中集中初始化(fwScene::InitClass 注册的场景接口回调)src/dev_ng/game/scene/scene.cpp:377-519、src/dev_ng/game/scene/scene.cpp:594-595。容量绝大多数是 CONFIGURED_FROM_FILE,即由运行时配置文件给出(配置数据不在 src 树内,本页未能核实具体数值)。FW_INSTANTIATE_CLASS_POOL_SPILLOVER 的第三个参数是溢出比例:

池实例化证据
CBuildingspillover 0.16,名称 "Building"src/dev_ng/game/scene/Building.cpp:24
CAnimatedBuildingspillover 0.55src/dev_ng/game/scene/AnimatedBuilding.cpp:35
CDummyObjectspillover 0.24(注释“was 7000”)src/dev_ng/game/Objects/DummyObject.cpp:28
CObjectFW_INSTANTIATE_BASECLASS_POOL,槽大小 LARGEST_OBJECT_CLASS = max(sizeof(CProjectileRocket), sizeof(CDoor)),并断言 CPickup 也放得下src/dev_ng/game/Objects/Object.cpp:127-133
CCompEntityspillover 0.69src/dev_ng/game/scene/entities/compEntity.cpp:41
CEntityBatch/CGrassBatch普通池src/dev_ng/game/scene/EntityBatch.cpp:74-75
CInteriorInstspillover 0.44src/dev_ng/game/scene/portals/InteriorInst.cpp:56
CPortalInstspillover 0.34src/dev_ng/game/scene/portals/PortalInst.cpp:26
CLightEntity分页池 FW_INSTANTIATE_CLASS_PAGEDPOOL,页大小 64src/dev_ng/game/renderer/Lights/LightEntity.cpp:61
CVehicle不是 fwPool:INSTANTIATE_POOL_ALLOCATOR(CVehicle)src/dev_ng/game/Vehicles/Vehicle.cpp:238

导航网格导出构建(NAVMESH_EXPORT 且将导出碰撞)会改用更大的固定容量:CBuilding 80000、CObject 11000、CDummyObject 30000、entity desc 池 65536 src/dev_ng/game/scene/scene.cpp:404-416。PS3 的 POOLS_IN_SYSMEMCONTAINER 会把 dummy 等池放进可被系统回收的 5MB sysmem 容器 src/dev_ng/game/scene/scene.cpp:384-402、src/dev_ng/game/scene/scene.cpp:435-458。REGREF_VALIDATION 构建下 CScene::TidyReferences 每帧遍历建筑池的 1/1500、dummy 池的 1/500 清理失效的已知引用(注释给出的池规模为 44000 与 15000,这是注释信息而非配置证据)src/dev_ng/game/scene/scene.cpp:932-968。

2.2 类型 → 池查找表#

CEntity::InitPoolLookupArray 填充 ms_EntityPools[ENTITY_TYPE_TOTAL];NOTINPOOLS、LIGHT 为 NULL,VEHICLE 与 OBJECT 也不在表中,注释写道“CVehicles and Objects do NOT use fwPool anymore!” src/dev_ng/game/scene/Entity.cpp:3853-3873;GetEntityPoolFromType 对这两类断言 src/dev_ng/game/scene/Entity.h:856-860。

证据差异:注释对 CVehicle 成立(池分配器),但 CObject 实际仍使用 FW_INSTANTIATE_BASECLASS_POOL(fwPool 的基类池变体,允许派生类 CDoor/CPickup/投射物共用同一池),只是没有登记到查找表中 src/dev_ng/game/Objects/Object.cpp:133。CObjectPopulation 等代码也直接以 CObject::GetPool() 遍历 src/dev_ng/game/Objects/objectpopulation.cpp:839-843。

2.3 实体选择 ID#

CEntityIDHelper 把实体编码为 entitySelectID:32 位版本为 4 位类型 + 5 位着色器索引 + 23 位池槽位;64 位版本为 5 + 7 + 25 位 src/dev_ng/game/scene/EntityId.h:33-54。GetEntityIDFromPool 用池的 GetJustIndex 取槽位 src/dev_ng/game/scene/EntityId.h:90-104;反查时 GetEntityFromPool 会临时关闭主线程内存校验以便渲染线程调用 src/dev_ng/game/scene/EntityId.h:57-88。这是渲染侧“实体拾取/选择”缓冲的基础(ENTITYSELECT,调试工具见 ../23-debug-security/debug-tools.md)。

3. 加入与移出世界#

3.1 世界表示的装配#

CGameWorld::Init(INIT_CORE) 做三件事 src/dev_ng/game/scene/world/GameWorld.cpp:390-449:

  1. fwSceneUpdate::Init 并注册 15 个游戏定义的 scene update 位及回调(SU_UPDATE 与 SU_UPDATE_VEHICLE 共用 SceneUpdateUpdateCb,SU_TRAIN 无回调)src/dev_ng/game/scene/world/GameWorld.cpp:394-409。位定义从 fwSceneUpdate::USER_SCENE_UPDATE 开始左移 src/dev_ng/game/scene/world/GameWorld.h:72-89。
  2. 设置 fwEntityContainer::SetUpdateEntityDescFunc(CGameWorld::UpdateEntityDesc),让框架容器缓存实体包围盒时回调游戏逻辑 src/dev_ng/game/scene/world/GameWorld.cpp:411-412。
  3. 创建唯一的默认世界表示 fwWorldRepMulti,处理 BUILDING…COMPOSITE 等类型掩码(不含 INSTANCE_LIST、GRASS_INSTANCE_LIST、VEHICLEGLASSCOMPONENT)与全部操作掩码,交给 gWorldMgr.Initialise src/dev_ng/game/scene/world/GameWorld.cpp:414-449。批次实体如何入场景图本页未追踪(见“未决问题”)。

INIT_BEFORE_MAP_LOADED 初始化 CInstancePriority;INIT_SESSION 复位冻结标志并默认打开 ped/vehicle/object 的“通过动画队列更新” src/dev_ng/game/scene/world/GameWorld.cpp:463-477。这些 Init/Shutdown 通过 gameSkeleton 注册(REGISTER_INIT_CALL(CGameWorld::Init, ...))src/dev_ng/game/Core/game.cpp:2108,INIT_CORE 还在 CScene::Init 中直接调用 src/dev_ng/game/scene/scene.cpp:651。

UpdateEntityDesc 必须在场景图未锁时调用(SceneGraphLockedFatalAssert);动态实体使用“扩展包围盒”(玻璃等需要更宽的边界),灯光与门户直接取包围盒/球,其余实体走更快的 GetBounds src/dev_ng/game/scene/world/GameWorld.cpp:5779-5822。

3.2 CGameWorld::Add / Remove 时序#

证据:Add 的断言与分支 src/dev_ng/game/scene/world/GameWorld.cpp:682-761;CEntity::Add/AddWithRect → gWorldMgr.AddToWorld src/dev_ng/game/scene/Entity.cpp:1614-1636;静态实体入内景 AppendObjectInstance src/dev_ng/game/scene/Entity.cpp:1591-1609;动态实体入内景 AddObjectInstance src/dev_ng/game/scene/DynamicEntity.cpp:175-192;PostAdd src/dev_ng/game/scene/world/GameWorld.cpp:780-828。

要点:

  • “已在世界中”的定义:被内景代理保留(GetIsRetainedByInteriorProxy)或拥有 fwEntityContainer;重复添加会断言并返回 src/dev_ng/game/scene/world/GameWorld.cpp:727-730、src/dev_ng/game/scene/Entity.cpp:637-639。
  • 内景实体不在外景扇区列表中,而是挂在 CInteriorInst 上,后者自身在外景世界表示里 src/dev_ng/game/scene/world/GameWorld.cpp:748-756。CObject 不能走 CEntity::AddToInterior(它们有 portal tracker,走房间的“活动对象列表”)src/dev_ng/game/scene/Entity.cpp:1598。
  • 音频线程不能安全读取实体内景位置,因此 Add 时缓存到实体 src/dev_ng/game/scene/world/GameWorld.cpp:758-759。
  • PostAdd 仅对 RequiresProcessControl() && GetIsDynamic() 的实体加入 scene update;梯子(GetIsLadder 的建筑/动画建筑)登记到 CTaskGoToAndClimbLadder;带 bHasSpawnPoints 的实体由 AddScenarioPointsForEntity 从 2dEffect 的 ET_SPAWN_POINT 生成场景点(CObject/CBuilding 先尝试场景点覆盖)src/dev_ng/game/scene/world/GameWorld.cpp:786-808、src/dev_ng/game/scene/world/GameWorld.cpp:831-878。

Remove 先记录 ms_pEntityBeingRemoved;被内景代理保留的实体走 CInteriorProxy::CleanupRetainedEntity,否则按 bInMloRoom 选 RemoveFromInterior 或 Remove;随后 PostRemove(移出 scene update、移除物理、场景点、梯子),并在需要时把动态障碍移出路径服务器 src/dev_ng/game/scene/world/GameWorld.cpp:885-954、src/dev_ng/game/scene/world/GameWorld.cpp:956-1020。RemoveAndAdd 用 ms_pEntityBeingRemovedAndAdded 标记,使场景点等不被拆除重建 src/dev_ng/game/scene/world/GameWorld.cpp:1022-1035。

3.3 位置变化后的增量更新#

实体移动后调用 UpdateWorldFromEntity(RequestUpdateInWorld 的实现):在内景房间中则 CInteriorInst::UpdateObjectInstance;在外景且有 fwDynamicEntityComponent 时直接 CGameWorld::UpdateEntityDesc,否则 gWorldMgr.UpdateInWorld;位置超出 g_WorldLimits_AABB 会断言 src/dev_ng/game/scene/Entity.cpp:1683-1721。

3.4 删除实体#

CGameWorld::RemoveEntity 按类型分派:ped 交给 CPedFactory::DestroyPed/DestroyPlayerPed;object 先 ClearRelatedDummy 再 CObjectPopulation::DestroyObject;vehicle 交给 CVehicleFactory::Destroy;其他类型直接 Remove + delete src/dev_ng/game/scene/world/GameWorld.cpp:3692-3730。会话关闭时 Shutdown2 用 gWorldMgr.GetAll 取最多 10000 个实体倒序删除(注释承认“恰好满足依赖顺序,如履薄冰”),Shutdown3 再清空建筑与 dummy 池 src/dev_ng/game/scene/world/GameWorld.cpp:349-385。

4. 每帧实体处理#

4.1 在主循环中的位置#

CGameWorld 的若干阶段分布在 CGame 的更新组中(normal 模式的注册顺序):

顺序注册调用说明证据
1CStreaming::Update(“Streaming”组)流式与 IMAP 请求src/dev_ng/game/Core/game.cpp:2760-2767
2CObjectPopulationNY::Process(“World and Population/Objects”)dummy↔object 转换src/dev_ng/game/Core/game.cpp:2797-2801
3CScene::Update内含 CGameWorld::Process 等src/dev_ng/game/Core/game.cpp:2826
4CTheScripts::Process脚本src/dev_ng/game/Core/game.cpp:2860-2866
5CGameWorld::ProcessAfterAllMovement所有移动(含脚本)之后src/dev_ng/game/Core/game.cpp:2872-2878
6camManager::Update相机在对象移动之后src/dev_ng/game/Core/game.cpp:2899-2900
7CGameWorld::ProcessPedsEarlyAfterCameraUpdateped 尽早启动相机后动画src/dev_ng/game/Core/game.cpp:2919-2923
8CGameWorld::ProcessAfterCameraUpdate相机后可见性、动画、IKsrc/dev_ng/game/Core/game.cpp:2958-2962
9CSceneStreamerMgrWrapper::PreScanUpdate → CGameScan::KickVisibilityJobs场景流式与可见性作业src/dev_ng/game/Core/game.cpp:2974、src/dev_ng/game/Core/game.cpp:3006
10CGameWorld::ProcessAfterPreRender渲染前处理之后(由 Renderer.cpp 调用)src/dev_ng/game/renderer/Renderer.cpp:1008

CScene::Update 在玩家已定位(CStreaming::IsPlayerPositioned())时依次执行 CPortal::Update、CCompEntity::Update、g_LoadScene.Update、CPopCycle::Update、CGameWorld::Process,随后处理车辆/武器 HD 请求、CEntityBatch::ResetHdEntityVisibility、路径、g_PlayerSwitch.Update、路径服务器、掩体、拾取物、CGlassPaneManager::Update 等;无论是否定位都会执行 CGameScan::Update 与 COcclusion::Update;最后(已定位时)TidyReferences 与 CDoorSystem::Update src/dev_ng/game/scene/scene.cpp:787-863。

4.2 CGameWorld::Process#

src/dev_ng/game/scene/world/GameWorld.cpp:1802-2174 的执行顺序:

  1. 游戏暂停则 ProcessPaused 并返回(回放模式下可强制做一次动画更新)src/dev_ng/game/scene/world/GameWorld.cpp:1820-1833。
  2. SetFreezeFlags、CExpensiveProcessDistributer::Update、冲击事件扫描、ProcessGlobalEvents、清空冻结列表 src/dev_ng/game/scene/world/GameWorld.cpp:1838-1848。
  3. 物理前动画:StartAnimationUpdate 清空 fwAnimDirector 的物理前队列,全局锁定 motion tree(锁定期间禁止读写骨骼局部矩阵、禁止删除带 anim director 的动态实体),更新同步场景相位,然后跑 SU_START_ANIM_UPDATE_PRE_PHYSICS 与 ..._PASS2 src/dev_ng/game/scene/world/GameWorld.cpp:1155-1175。动画在工作线程上异步执行。
  4. AI/控制更新:分配栈上删除列表(MAX_ENTITIES_TO_DELETE_PER_FRAME=512)与“移出处理列表”列表(MAX_ENTITIES_TO_REMOVE_FROM_PROCCONT_LIST=16),AllowDelete(false),设置 PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(true) 以省去物理锁;CPed::StartAiUpdate 后执行 fwSceneUpdate::Update(SU_UPDATE),车辆场景管理器更新后执行 SU_UPDATE_VEHICLE,然后 UpdateEntitiesThroughAnimQueue 与 aiDeferredTasks::RunAll src/dev_ng/game/scene/world/GameWorld.cpp:1675-1676、src/dev_ng/game/scene/world/GameWorld.cpp:1879-1972。__BANK 的按类型显示模式改为逐类型调用 UpdateEntityType src/dev_ng/game/scene/world/GameWorld.cpp:1920-1943。
  5. 战斗管理器、CTaskCombat/CTaskWander/CTaskSmartFlee::UpdateClass、关系组 src/dev_ng/game/scene/world/GameWorld.cpp:1981-1996。
  6. EndAnimationUpdate:阻塞等待所有动画更新完成,跑 SU_END_ANIM_UPDATE_PRE_PHYSICS,解锁 motion tree src/dev_ng/game/scene/world/GameWorld.cpp:1177-1190。
  7. 延迟删除:AllowDelete(true) 下逐个 RemoveEntity,再把返回“不再需要处理”的实体移出 scene update src/dev_ng/game/scene/world/GameWorld.cpp:2005-2032。
  8. CPedGroups::Process、CPhysics::UpdateRequests、CPhysics::Update(物理模拟),网络对局与单机预算分别为 6.0/7.5ms 的 timebar src/dev_ng/game/scene/world/GameWorld.cpp:2034-2062。
  9. 车辆录像、脚本任务记录;掉出世界清理按系统帧计数轮转:&7==1 清 ped,&7==3 清车,其余帧清对象 src/dev_ng/game/scene/world/GameWorld.cpp:2145-2155;最后缓存本地玩家通缉等级/健康等给其他线程读取 src/dev_ng/game/scene/world/GameWorld.cpp:2167-2173。

处理回调 SceneUpdateUpdateCb。 对带 REMOVE_FROM_WORLD 基础标志的实体只加入删除列表(满 512 即丢弃本帧请求);冻结实体调用 ProcessFrozen;否则若有网络对象先调用 CNetObjEntity::ProcessControl,非网络克隆再调用实体自身 ProcessControl();返回 false 的实体被收集起来(最多 16 个/帧)移出处理列表——注释说明多数对象静止后即如此 src/dev_ng/game/scene/world/GameWorld.cpp:2809-2897。

动画队列驱动的更新。 UpdateEntitiesThroughAnimQueue 从物理前动画队列 Pop():哪个实体的动画先完成就先对其执行 SceneUpdateUpdateCb,从而让 AI 更新与动画工作线程交叠;不再满足条件(ShouldUpdateThroughAnimQueue 按类型开关)的实体退回常规 scene update src/dev_ng/game/scene/world/GameWorld.cpp:1192-1299、src/dev_ng/game/scene/world/GameWorld.cpp:1634-1640。!__FINAL 下可改为忙等 PopIfAvailable(PS3 插入 __nop)以统计停顿次数 src/dev_ng/game/scene/world/GameWorld.cpp:1219-1267。车辆动画调度排在 ped 之后,保证司机的 ProcessControl 先于车辆 src/dev_ng/game/Vehicles/Vehicle.cpp:15959-15965。动画系统本身见 ../10-animation/game-animation-and-ik.md。

4.3 内景冻结#

当主视点位于某内景房间(CPortalVisTracker 的主内景/房间)、非联网、且房间标志含 ROOM_FREEZEPEDS/ROOM_FREEZEVEHICLES 时,只有在该内景 0 号房间(外部)所有非链接门户上的门都关闭的情况下才冻结外景 ped/车辆 src/dev_ng/game/scene/world/GameWorld.cpp:1409-1477。任务 ped、警察、刚从内景出来 10 秒内(InteriorFreezeWasInsideCounter=10000 ms)的 ped,以及任务车、警车、直升机、飞机、载有任务 ped 或玩家的车不会被冻结;冻结在联网时断言不得发生 src/dev_ng/game/scene/world/GameWorld.cpp:1484-1557。火车永不加入冻结列表 src/dev_ng/game/scene/world/GameWorld.cpp:1646-1667。

4.4 移动之后、相机之后、PreRender 之后#

  • ProcessAfterAllMovement:等待 Verlet(布料/绳索)任务,再次声明仅主线程更新物理,跑 SU_AFTER_ALL_MOVEMENT(同步场景),然后倒序遍历车辆池:处理附着、座椅碰撞、补加路径服务器障碍、UpdatePortalTracker,以及处理 bIsOutOfMap 车辆;ped 等随后处理(同函数后半段)src/dev_ng/game/scene/world/GameWorld.cpp:2182-2260。注释说明它必须在所有会移动东西的代码(含脚本)之后执行 src/dev_ng/game/Core/game.cpp:2872-2876。
  • ProcessPedsEarlyAfterCameraUpdate:锁定 motion tree,对每个 ped 调用 ProcessPostCamera,启动“即时动画更新”的 ped 记入 ms_pedsRunningInstantAnimUpdate src/dev_ng/game/scene/world/GameWorld.cpp:2423-2457。
  • ProcessAfterCameraUpdate:车辆与 CObject::ms_ObjectsThatNeedProcessPostCamera 的相机后处理(倒序遍历以容忍 DeleteFast)、结束即时动画、刷新可见性帧计数、相机后动画(SU_START_ANIM_UPDATE_POST_CAMERA)、更新 VFX 相机缓存、CBaseIkManager::ProcessSolvers src/dev_ng/game/scene/world/GameWorld.cpp:2464-2536。
  • ProcessAfterPreRender:等待动画线程、解锁、CAnimSceneManager PreRender 阶段;ped PostPreRender → 延迟的附着树 → 武器 → ped 附着后处理 → 投射物 → 武器管理器 → 碎玻璃;期间 CBreakable::SetInsertDirectToRenderList(true) 使碎裂产生的新对象直接进入渲染列表 src/dev_ng/game/scene/world/GameWorld.cpp:2540-2641。

4.5 掉出世界的实体#

RemoveFallenPeds 用 ped 空间数组查询 z < LOWESTPEDZ(-200) 的 ped(每次最多 192 个),非车内 ped 交给 RemoveOutOfMapPed(尝试救回或删除);车辆与对象有对应常量 LOWESTCARZ/LOWESTOBJECTZ = -200,对象分 25 帧遍历 src/dev_ng/game/scene/world/GameWorld.cpp:3293-3309、src/dev_ng/game/scene/world/GameWorld.cpp:3426-3427、src/dev_ng/game/scene/world/GameWorld.cpp:3568-3569。

4.6 其余 CGameWorld 服务#

服务API说明 / 证据
空间搜索ForAllEntitiesIntersecting(fwSearchVolume*, cb, data, typeFlags, locationFlags, lodFlags, optionFlags, module)类型掩码用 ENTITY_TYPE_MASK_*;旧式 FindObjectsInRange 等被注释标为待淘汰 src/dev_ng/game/scene/world/GameWorld.h:158-178
清理区域ClearCarsFromArea/ClearPedsFromArea/ClearObjectsFromArea/ClearExcitingStuffFromArea供脚本命令使用 src/dev_ng/game/scene/world/GameWorld.h:189-197
玩家查找FindLocalPlayer*、FindFollowPlayer*(观战时返回被观战者)本地玩家实际保存在 CPedFactory src/dev_ng/game/scene/world/GameWorld.h:229-268、src/dev_ng/game/scene/world/GameWorld.h:366-381
换玩家模型ChangePedModel、ChangePlayerPed(保存/恢复掩体任务)被玩家切换等使用 src/dev_ng/game/scene/world/GameWorld.h:220-227
高度图CGameWorldHeightMap::GetMin/MaxHeightFromWorldHeightMap 等世界最低/最高高度栅格,HEIGHTMAP_TOOL 构建可重新生成 src/dev_ng/game/scene/world/GameWorldHeightMap.h:21-128

5. game/Objects:道具、门与对象人口#

5.1 CObject#

CObject 是可被物理模拟的道具(也是门、拾取物、投射物、武器对象的基类)。构造函数受保护,注释要求“使用 CObjectPopulation::CreateObject 创建” src/dev_ng/game/Objects/object.h:175-196。它与地图 dummy 互相引用:GetRelatedDummy/SetRelatedDummy(pDummy, loc) src/dev_ng/game/Objects/object.h:215-218;脚本“脑”状态机(OBJECT_WAITING_TILL_IN_BRAIN_RANGE 等)用于世界点脚本 src/dev_ng/game/Objects/object.h:183-190;IsADoor()/IsPickup() 通过 CObjectFlags 判断 src/dev_ng/game/Objects/object.h:437-438。

CObjectPopulation::CreateObject(const CreateObjectInput&) 在 archetype GetUsesDoorPhysics() 且非碎片缓存/过场所有时创建 CDoor,否则 CObject;池满时先 TryToFreeUpTempObjects(5) 再重试,仍失败则 DumpObjectPool 并报错 src/dev_ng/game/Objects/objectpopulation.cpp:331-359。

5.2 Dummy ↔ Object 转换(对象人口)#

CObjectPopulationNY::Process → CObjectPopulation::Process → ManageObjectPopulation(false) src/dev_ng/game/Objects/objectpopulationNY.cpp:44-48、src/dev_ng/game/Objects/objectpopulation.cpp:285-288:

  1. 以 CFocusEntityMgr 位置为中心;暂停时(非回放编辑)直接返回 src/dev_ng/game/Objects/objectpopulation.cpp:811-830。
  2. 真实对象:每帧处理已用槽数 / FRAMESTOCOMPLETEMANAGEOBJECTPOPULATION(8) 个;跳过“钉住”的 never-dummy、玩家手持、程序化对象;对每个调用 ManageObject(可能转回 dummy 或删除)src/dev_ng/game/Objects/objectpopulation.cpp:808-809、src/dev_ng/game/Objects/objectpopulation.cpp:839-887。
  3. dummy:先处理高优先级 dummy,再每帧处理 dummy 已用槽数 / FRAMESTOCOMPLETEMANAGEDUMMYPOPULATION(32) 减去高优先级数量;然后处理被抑制的 dummy、脚本强制转换体积、never-dummy 强制转换 src/dev_ng/game/Objects/objectpopulation.cpp:895-933。
  4. ManageDummy:可见且已入世界的 dummy,若距离 < OBJECT_POPULATION_RESET_RANGE(80m)、被狙击镜需要、或允许钉住且 never-dummy,并且(不在 LOD 层次中 / 模型已加载 / never-dummy 且无自动动画)则 ConvertToRealObject src/dev_ng/game/Objects/objectpopulation.h:29-34、src/dev_ng/game/Objects/objectpopulation.cpp:1121-1157。狙击判定要求第一人称瞄准、实体在 LOD 可见范围内且包围球在游戏视口内 src/dev_ng/game/Objects/objectpopulation.cpp:1165-1181。
  5. ConvertToRealObject:pDummy->CreateObject() → dummy Disable()(不可见、不可搜索)→ 传播扩展 → SetRelatedDummy → CGameWorld::Add(pObject, dummy 的 interiorLocation) → 通知使用该场景实体的任务 → 对与新对象 AABB 相交且位于其 OBB 内的随机 ped 关闭与该对象的碰撞(bug 2068426 的修复)src/dev_ng/game/Objects/objectpopulation.cpp:1307-1416。

IMAP 卸载时 CMapDataContents::Remove 会先找出 m_iplIndex 等于该槽位的 CObject:有相关 dummy 的恢复 dummy 并解除关联,然后经 CObjectPopulation::DestroyObject 删除(以便网络层处理)src/dev_ng/game/scene/loader/MapData.cpp:182-208。

证据:创建见 src/dev_ng/game/modelinfo/BaseModelInfo.cpp:259-265;转换函数声明见 src/dev_ng/game/Objects/objectpopulation.h:103-112;卸载见 src/dev_ng/game/scene/loader/MapData.cpp:178-239。

CFileLoader::MarkArchetypesWhichCannotBeDummy 用一张硬编码模型表把蒙皮动画道具(注释举例抽油机、风车)标为不能以 dummy 渲染 src/dev_ng/game/scene/FileLoader.cpp:2002-2010;后扫描阶段对这类 never-dummy 对象改用其相关 dummy 的 LOD 父节点做淡入淡出 src/dev_ng/game/renderer/PostScan.cpp:1047-1061。

5.3 门:CDoor 与 CDoorSystem#

CDoor 区分标准门、水平/垂直滑动门、车库门、栏杆臂、铁路道口栏杆,以及它们的 _SC 变体 src/dev_ng/game/Objects/Door.h:47-64;控制标志包括不回弹、关闭时上锁、双开门检测、过场不关闭、已闩锁、卡住等 src/dev_ng/game/Objects/Door.h:68-79。

门对象会随 dummy/object 转换反复创建销毁,因此门的持久状态独立存于 CDoorSystem:

  • CDoorSystemData 继承 CDoorScriptHandlerObject(可注册到脚本句柄、可联网),保存位置、模型哈希、DoorScriptState(UNLOCKED/LOCKED/FORCE_LOCKED_UNTIL_OUT_OF_AREA/FORCE_*_THIS_FRAME 等)、待生效状态、目标开合比、自动开门参数、破损标志等 src/dev_ng/game/Objects/Door.h:389-470。
  • CDoorSystem 用两张 atMap<u32, CDoorSystemData*>(门表与锁表),提供按枚举哈希/实体/位置查找、LinkDoorObject/UnlinkDoorObject、SetLocked 等 src/dev_ng/game/Objects/Door.h:699-756。
  • 位置哈希 GenerateHash32 把 z(-200..823m,4m 精度)、y、x 量化后打包,断言坐标在世界表示范围内 src/dev_ng/game/Objects/Door.cpp:5715-5735。
  • LinkDoorObject:新门实体按模型的脚本搜索半径找到系统数据;若已有更接近记录位置的门则不替换;把当前或待生效的非解锁状态设为 pending 并立即应用;同步破损/损坏标志 src/dev_ng/game/Objects/Door.cpp:5187-5240。
  • CDoorSystem::Update(由 CScene::Update 调用)每帧对有实体的门应用 pending 状态,并删除标记移除的记录(单帧最多 50 个);源码中同步碎裂状态的分支被 if (0) 禁用 src/dev_ng/game/Objects/Door.cpp:5506-5560、src/dev_ng/game/scene/scene.cpp:862。

门的手感由数据驱动:DOOR_TUNING_FILE 在关卡加载时读取 src/dev_ng/game/scene/FileLoader.cpp:1559-1568,CDoorTuning 字段包括自动开门体积偏移/半径倍率/速率、触发盒、可被车辆撞断及断裂冲量、闩锁、质量倍率、旋转限制角、标准门开启方向等,模型通过 ModelToTuneName 映射到命名调参 src/dev_ng/game/Objects/DoorTuning.psc:24-61。

5.4 其他 Objects 子系统#

组件作用证据
CProcObjectMan根据地表材质的 proc tag 在三角形上程序化生成道具,上限 MAX_PROC_COBJECTS=200、MAX_PROC_CBUILDINGS=2300、移除三角形 MAX_REMOVED_TRIS PC 3072/其他 1024;在植物管理器之后的 “Proc Objects” 更新中运行src/dev_ng/game/Objects/ProcObjects.h:92-104、src/dev_ng/game/Core/game.cpp:2971-2972
CProceduralInfoproc tag 表:MAX_PROCEDURAL_TAGS=255,其中 128 起为植物;MAX_PROC_OBJ_INFOS=272、MAX_PLANT_INFOS=112src/dev_ng/game/Objects/ProceduralInfo.h:38-42
CObjectIntelligence对象的任务/智能组件(独立池,CGtaSceneInterface::InitPools 中初始化)src/dev_ng/game/Objects/ObjectIntelligence.h:15、src/dev_ng/game/scene/scene.cpp:477
CoverTuning道具掩体调参(OBJ_COVER_TUNING_FILE)src/dev_ng/game/scene/FileLoader.cpp:1578

6. scene/entities:复合实体 CCompEntity(Rayfire)#

复合实体用于“可破坏/可动画的大型地图事件”(源码调试输出称为 Rayfire):一个 CCompEntity 拥有最多 10 个子 CObject(注释:最多 8 个动画),在“开始模型 → 动画模型 + 特效 → 结束模型”之间切换 src/dev_ng/game/scene/entities/compEntity.h:122-123、src/dev_ng/game/scene/entities/compEntity.h:199-212。其类型数据 CCompositeEntityType 来自 ITYP(开始/结束模型、可选的开始/结束 IMAP、PTFX 资产、动画列表与每个动画的特效数据)src/dev_ng/game/scene/loader/mapTypes.h:104-158。

  • 调度:CCompEntity::Update 每帧扫描池中 1/COMPENTITY_UPDATE_NUM_FRAMES_TO_PROCESS(4) 的槽位:与相机距离小于 LOD 距离的加入活动列表并恢复子对象;超出且未在动画中的移出并删除 drawable;然后对活动列表逐个 ImmediateUpdate src/dev_ng/game/scene/entities/compEntity.h:18-19、src/dev_ng/game/scene/entities/compEntity.cpp:432-503。
  • 状态机 eCompEntityState(16 个状态,回放构建另加 2 个)src/dev_ng/game/scene/entities/compEntity.h:21-45;资产集 CE_START/ANIM/PTFX/END_ASSETS 通过 strRequest/strRequestArray<20> 流式请求 src/dev_ng/game/scene/entities/compEntity.h:47-53、src/dev_ng/game/scene/entities/compEntity.h:199-203。

证据:src/dev_ng/game/scene/entities/compEntity.cpp:640-781(SetToStarting 设置 SYNC_STARTING、TriggerAnim 设置 START_ANIM 由 compEntity.cpp:232-355 的 setter 完成,本页未逐行展开)。动画期间子对象被设 bForcePrePhysicsAnimUpdate,保证即使不在屏幕上也更新 src/dev_ng/game/scene/entities/compEntity.cpp:705-714。INIT 状态依据 fwMapDataStore 中开始/结束 IMAP 的可流式状态推断当前处于哪一端,从而在重新流入时与已执行的 IMAP 组切换保持一致 src/dev_ng/game/scene/entities/compEntity.cpp:641-667。脚本命令表见 ../14-script-runtime/natives-and-commands.md。

7. scene/decorator(游戏侧)#

游戏侧只有一个很薄的 CDecoratorInterface : fwDecoratorInterface:在 DECORATOR_DEBUG & VOLUME_SUPPORT 时实现 GetOffset,让调试绘制能定位装饰器宿主——宿主若是 fwEntity 取其变换位置,若是 CVolume 取体积位置 src/dev_ng/game/scene/decorator/decoratorInterface.h:13-25、src/dev_ng/game/scene/decorator/decoratorInterface.cpp:22-46。装饰器的存储与脚本命令属于框架 fwDecorator,见 ../04-rage-framework/entity-and-archetype.md。

约束与边界行为#

  • 线程:CGameWorld::Add 对 ped 断言必须在更新线程;AddToSceneUpdate/RemoveFromSceneUpdate 断言必须在主线程 src/dev_ng/game/scene/world/GameWorld.cpp:687、src/dev_ng/game/scene/DynamicEntity.cpp:597、src/dev_ng/game/scene/DynamicEntity.cpp:659。
  • 动画锁窗口:StartAnimationUpdate 到 EndAnimationUpdate 之间,以及 ProcessPedsEarlyAfterCameraUpdate 到 ProcessAfterPreRender 之间,不能删除带 anim director 的动态实体或读写其骨骼局部矩阵 src/dev_ng/game/scene/world/GameWorld.cpp:1162-1166、src/dev_ng/game/scene/world/GameWorld.cpp:2429-2433。这正是 Process 采用延迟删除列表的原因(源码注释:直接删除会崩溃 src/dev_ng/game/scene/world/GameWorld.cpp:1881-1882)。
  • 删除容量:单帧最多延迟删除 512 个、移出处理列表 16 个,超出部分留到下一帧 src/dev_ng/game/scene/world/GameWorld.cpp:2821-2827、src/dev_ng/game/scene/world/GameWorld.cpp:2886-2891。
  • 冻结仅单机:内景冻结在 NetworkInterface::IsGameInProgress() 时不启用,并有断言 src/dev_ng/game/scene/world/GameWorld.cpp:1413、src/dev_ng/game/scene/world/GameWorld.cpp:1551-1554。
  • dummy 不进内景:m_iplIndex > 0 的 CDummyObject 即使给出有效内景位置也加入外景 src/dev_ng/game/scene/world/GameWorld.cpp:743-746(推断:内景中的道具 dummy 由 CInteriorInst 自己管理,见内景页)。

未决问题 / 证据不足#

  • 各池的实际容量来自运行时配置文件(CONFIGURED_FROM_FILE),该文件不在 src 树中,本页没有给出数值。
  • fwWorldRepMulti 的处理类型掩码不含 INSTANCE_LIST/GRASS_INSTANCE_LIST;这些批次实体如何进入场景图(推测由框架 fwMapDataContents::Entities_AddToWorld 走单独路径)本页未追踪到框架实现。
  • ManageObject/ConvertToDummyObject 的具体条件(距离、可见性、网络限制)只列出入口,未逐条展开。
  • GameWorldWaterHeight、WorldDebugHelper、scene/debug/*(PostScanDebug、SceneCostTracker、SceneIsolator 等)属于调试/工具代码,本页只提及存在。