世界与实体:CGameWorld、CEntity 类层次、场景实体与对象
本页解释 GTAV 游戏层如何表示“世界中的东西”:CEntity 及其派生类如何在 RAGE 框架的 fwEntity 之上扩展、实体如何被放入/移出世界(外景空间结构或内景房间)、每帧的实体处理循环(CGameWorld::Process 及其 scene-update 回调)、实体池与类型标识,以及 game/Objects(CObject/CDummyObject/CDoor/对象人口)和 scene/entities(CCompEntity 等)这些具体实体族。
背景与相关页面#
- 框架层的
fwEntity/fwArchetype/扩展机制见../04-rage-framework/entity-and-archetype.md;gWorldMgr、场景图、扫描与搜索见../04-rage-framework/scene-graph-and-visibility.md。本页只描述游戏侧如何使用它们。 - 池(
fwPool、CONFIGURED_FROM_FILE)与内存桶见../01-runtime/memory-and-pools.md;gameSkeleton的更新组注册见../01-runtime/game-skeleton-and-main-loop.md。 - 地图实体从 IMAP 实例化的过程见
map-data-and-lod.md;内景(CInteriorInst/CPortalInst/CPortalTracker)见interiors-and-portals.md;模型信息(archetype)见model-info.md。 CPed、CVehicle、物理细节分别见../06-peds/ped-architecture.md、../08-vehicles/vehicle-architecture.md、../09-physics/game-physics-and-ragdoll.md。
相关源码文件#
| 文件 | 内容 |
|---|---|
game/scene/EntityTypes.h | eEntityType、eEntityOwnedBy 枚举与掩码 |
game/scene/Entity.h/.cpp | CEntity(游戏实体基类)、CEntityFlags、可见性模块、池查找表 |
game/scene/DynamicEntity.h/.cpp | CDynamicEntity(可动/可动画实体、portal tracker、scene update 注册) |
game/scene/Physical.h/.cpp | CPhysical(物理实体,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.cpp | CGtaSceneInterface::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_BUILDING | CBuilding | CEntity |
ENTITY_TYPE_ANIMATED_BUILDING | CAnimatedBuilding | CDynamicEntity : CEntity |
ENTITY_TYPE_VEHICLE | CVehicle | CPhysical : CDynamicEntity : CEntity |
ENTITY_TYPE_PED | CPed | CPhysical : CDynamicEntity : CEntity |
ENTITY_TYPE_OBJECT | CObject | CPhysical : CDynamicEntity : CEntity |
ENTITY_TYPE_DUMMY_OBJECT | CDummyObject | CEntity |
ENTITY_TYPE_PORTAL | CPortalInst | CBuilding : CEntity |
ENTITY_TYPE_MLO | CInteriorInst | CBuilding : CEntity |
ENTITY_TYPE_NOTINPOOLS | —— | 仅用于触发音频 |
ENTITY_TYPE_PARTICLESYSTEM | CPtFxSortedEntity | CEntity |
ENTITY_TYPE_LIGHT | CLightEntity | CEntity |
ENTITY_TYPE_COMPOSITE | CCompEntity | CEntity |
ENTITY_TYPE_INSTANCE_LIST | CEntityBatch | CEntityBatchBase : CEntity |
ENTITY_TYPE_GRASS_INSTANCE_LIST | CGrassBatch | CEntityBatchBase : CEntity |
ENTITY_TYPE_VEHICLEGLASSCOMPONENT | CVehicleGlassComponentEntity | CEntity |
每个类型都有对应的位掩码 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:
- scene update 成员资格:
AddToSceneUpdate/RemoveFromSceneUpdate,并通过fwSceneUpdate::IsInSceneUpdate查询src/dev_ng/game/scene/DynamicEntity.h:112-118。 - 动画:
CreateAnimDirector、StartAnimUpdate/EndAnimUpdate、骨骼更新src/dev_ng/game/scene/DynamicEntity.h:155-190。 - 内景追踪:内嵌
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 |
CInstanceListAssetLoader | KEEP_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.md | src/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 的第三个参数是溢出比例:
| 池 | 实例化 | 证据 |
|---|---|---|
CBuilding | spillover 0.16,名称 "Building" | src/dev_ng/game/scene/Building.cpp:24 |
CAnimatedBuilding | spillover 0.55 | src/dev_ng/game/scene/AnimatedBuilding.cpp:35 |
CDummyObject | spillover 0.24(注释“was 7000”) | src/dev_ng/game/Objects/DummyObject.cpp:28 |
CObject | FW_INSTANTIATE_BASECLASS_POOL,槽大小 LARGEST_OBJECT_CLASS = max(sizeof(CProjectileRocket), sizeof(CDoor)),并断言 CPickup 也放得下 | src/dev_ng/game/Objects/Object.cpp:127-133 |
CCompEntity | spillover 0.69 | src/dev_ng/game/scene/entities/compEntity.cpp:41 |
CEntityBatch/CGrassBatch | 普通池 | src/dev_ng/game/scene/EntityBatch.cpp:74-75 |
CInteriorInst | spillover 0.44 | src/dev_ng/game/scene/portals/InteriorInst.cpp:56 |
CPortalInst | spillover 0.34 | src/dev_ng/game/scene/portals/PortalInst.cpp:26 |
CLightEntity | 分页池 FW_INSTANTIATE_CLASS_PAGEDPOOL,页大小 64 | src/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:
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。- 设置
fwEntityContainer::SetUpdateEntityDescFunc(CGameWorld::UpdateEntityDesc),让框架容器缓存实体包围盒时回调游戏逻辑src/dev_ng/game/scene/world/GameWorld.cpp:411-412。 - 创建唯一的默认世界表示
fwWorldRepMulti,处理 BUILDING…COMPOSITE 等类型掩码(不含INSTANCE_LIST、GRASS_INSTANCE_LIST、VEHICLEGLASSCOMPONENT)与全部操作掩码,交给gWorldMgr.Initialisesrc/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 模式的注册顺序):
| 顺序 | 注册调用 | 说明 | 证据 |
|---|---|---|---|
| 1 | CStreaming::Update(“Streaming”组) | 流式与 IMAP 请求 | src/dev_ng/game/Core/game.cpp:2760-2767 |
| 2 | CObjectPopulationNY::Process(“World and Population/Objects”) | dummy↔object 转换 | src/dev_ng/game/Core/game.cpp:2797-2801 |
| 3 | CScene::Update | 内含 CGameWorld::Process 等 | src/dev_ng/game/Core/game.cpp:2826 |
| 4 | CTheScripts::Process | 脚本 | src/dev_ng/game/Core/game.cpp:2860-2866 |
| 5 | CGameWorld::ProcessAfterAllMovement | 所有移动(含脚本)之后 | src/dev_ng/game/Core/game.cpp:2872-2878 |
| 6 | camManager::Update | 相机在对象移动之后 | src/dev_ng/game/Core/game.cpp:2899-2900 |
| 7 | CGameWorld::ProcessPedsEarlyAfterCameraUpdate | ped 尽早启动相机后动画 | src/dev_ng/game/Core/game.cpp:2919-2923 |
| 8 | CGameWorld::ProcessAfterCameraUpdate | 相机后可见性、动画、IK | src/dev_ng/game/Core/game.cpp:2958-2962 |
| 9 | CSceneStreamerMgrWrapper::PreScanUpdate → CGameScan::KickVisibilityJobs | 场景流式与可见性作业 | src/dev_ng/game/Core/game.cpp:2974、src/dev_ng/game/Core/game.cpp:3006 |
| 10 | CGameWorld::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 的执行顺序:
- 游戏暂停则
ProcessPaused并返回(回放模式下可强制做一次动画更新)src/dev_ng/game/scene/world/GameWorld.cpp:1820-1833。 SetFreezeFlags、CExpensiveProcessDistributer::Update、冲击事件扫描、ProcessGlobalEvents、清空冻结列表src/dev_ng/game/scene/world/GameWorld.cpp:1838-1848。- 物理前动画:
StartAnimationUpdate清空fwAnimDirector的物理前队列,全局锁定 motion tree(锁定期间禁止读写骨骼局部矩阵、禁止删除带 anim director 的动态实体),更新同步场景相位,然后跑SU_START_ANIM_UPDATE_PRE_PHYSICS与..._PASS2src/dev_ng/game/scene/world/GameWorld.cpp:1155-1175。动画在工作线程上异步执行。 - 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::RunAllsrc/dev_ng/game/scene/world/GameWorld.cpp:1675-1676、src/dev_ng/game/scene/world/GameWorld.cpp:1879-1972。__BANK的按类型显示模式改为逐类型调用UpdateEntityTypesrc/dev_ng/game/scene/world/GameWorld.cpp:1920-1943。 - 战斗管理器、
CTaskCombat/CTaskWander/CTaskSmartFlee::UpdateClass、关系组src/dev_ng/game/scene/world/GameWorld.cpp:1981-1996。 EndAnimationUpdate:阻塞等待所有动画更新完成,跑SU_END_ANIM_UPDATE_PRE_PHYSICS,解锁 motion treesrc/dev_ng/game/scene/world/GameWorld.cpp:1177-1190。- 延迟删除:
AllowDelete(true)下逐个RemoveEntity,再把返回“不再需要处理”的实体移出 scene updatesrc/dev_ng/game/scene/world/GameWorld.cpp:2005-2032。 CPedGroups::Process、CPhysics::UpdateRequests、CPhysics::Update(物理模拟),网络对局与单机预算分别为 6.0/7.5ms 的 timebarsrc/dev_ng/game/scene/world/GameWorld.cpp:2034-2062。- 车辆录像、脚本任务记录;掉出世界清理按系统帧计数轮转:
&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_pedsRunningInstantAnimUpdatesrc/dev_ng/game/scene/world/GameWorld.cpp:2423-2457。ProcessAfterCameraUpdate:车辆与CObject::ms_ObjectsThatNeedProcessPostCamera的相机后处理(倒序遍历以容忍DeleteFast)、结束即时动画、刷新可见性帧计数、相机后动画(SU_START_ANIM_UPDATE_POST_CAMERA)、更新 VFX 相机缓存、CBaseIkManager::ProcessSolverssrc/dev_ng/game/scene/world/GameWorld.cpp:2464-2536。ProcessAfterPreRender:等待动画线程、解锁、CAnimSceneManagerPreRender 阶段;pedPostPreRender→ 延迟的附着树 → 武器 → 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:
- 以
CFocusEntityMgr位置为中心;暂停时(非回放编辑)直接返回src/dev_ng/game/Objects/objectpopulation.cpp:811-830。 - 真实对象:每帧处理已用槽数 /
FRAMESTOCOMPLETEMANAGEOBJECTPOPULATION(8)个;跳过“钉住”的 never-dummy、玩家手持、程序化对象;对每个调用ManageObject(可能转回 dummy 或删除)src/dev_ng/game/Objects/objectpopulation.cpp:808-809、src/dev_ng/game/Objects/objectpopulation.cpp:839-887。 - dummy:先处理高优先级 dummy,再每帧处理 dummy 已用槽数 /
FRAMESTOCOMPLETEMANAGEDUMMYPOPULATION(32)减去高优先级数量;然后处理被抑制的 dummy、脚本强制转换体积、never-dummy 强制转换src/dev_ng/game/Objects/objectpopulation.cpp:895-933。 ManageDummy:可见且已入世界的 dummy,若距离< OBJECT_POPULATION_RESET_RANGE(80m)、被狙击镜需要、或允许钉住且 never-dummy,并且(不在 LOD 层次中 / 模型已加载 / never-dummy 且无自动动画)则ConvertToRealObjectsrc/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。ConvertToRealObject:pDummy->CreateObject()→ dummyDisable()(不可见、不可搜索)→ 传播扩展 →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 |
CProceduralInfo | proc tag 表:MAX_PROCEDURAL_TAGS=255,其中 128 起为植物;MAX_PROC_OBJ_INFOS=272、MAX_PLANT_INFOS=112 | src/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;然后对活动列表逐个ImmediateUpdatesrc/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等)属于调试/工具代码,本页只提及存在。