直接销毁网格和碰撞体会导致卡顿,因触发同步GC、RHIThread等待及PhysX串行移除刚体;应改用对象池+隐藏+禁用碰撞,延迟调用ConditionalBeginDestroy()。

为什么直接销毁网格和碰撞体会导致卡顿
在开放大地图中频繁调用 DestroyActor() 或 UStaticMeshComponent::DestroyComponent() 卸载视野外网格,常引发主线程阻塞。根本原因不是“删得太多”,而是 Unreal 的资源释放路径会触发同步 GC、RHIThread 等待、以及物理世界(PhysX)的逐物体移除——尤其当一批含 UCapsuleComponent 或 UBoxComponent 的 Actor 被批量销毁时,FPhysScene::RemoveActorFromScene() 会串行遍历刚体列表并重置约束,开销陡增。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 避免每帧调用
DestroyActor();改用对象池 +SetActorHiddenInGame(true)+SetActorEnableCollision(false)快速“钝化”视野外 Actor - 对真正需释放内存的网格(如高模地形块),推迟到空闲帧或 GC 后一帧再调用
ConditionalBeginDestroy(),而非直接Destroy() - 确保
UStaticMesh的LODGroup已设为World或Terrain,否则卸载后 LOD0 资源仍被引用,GC 不回收
如何用 WorldPartition 和 Hierarchical LOD 管理动态加载/卸载
Unreal 5.1+ 的 WorldPartition 是目前最稳妥的方案,它把地图切分为 Grid Cell,由引擎自动管理加载/卸载边界。但默认行为不会自动卸载碰撞体——因为 UCollisionComponent 默认标记为 bAutoActivate=true 且未参与 WorldPartition 的可见性裁剪。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 启用
WorldPartition后,在Grid Cell的CellData中勾选bEnableCollisionCulling(需自定义UWorldPartitionRuntimeHash或打补丁) - 将地形静态网格设为
LODType = Hierarchical,并在HLODLayer中设置bIncludeInWorldPartition = true,这样 HLOD Proxy Actor 会在视野外自动切换为低精度碰撞体(甚至无碰撞) - 禁用
UStaticMeshComponent::bUseAsOccluder(除非真用于遮挡),否则即使隐藏也会参与 occlusion culling 计算,拖慢 CPU
手动实现网格卸载时,碰撞体怎么安全解绑而不崩
若因兼容旧项目无法用 WorldPartition,需手动管理,则核心矛盾是:网格组件和碰撞组件生命周期不一致。常见错误是先删 UStaticMeshComponent,再删 UCapsuleComponent,结果 PhysX 报错 "Invalid actor handle passed to RemoveActorFromScene"。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 统一用
UActorComponent::Deactivate()替代直接销毁;对碰撞体额外调用USceneComponent::DetachFromParent(false)断开层级依赖 - 在
AActor::EndPlay()中检查GetWorld()->IsNetMode(NM_DedicatedServer),服务端可跳过碰撞体清理(PhysX 不运行),客户端必须调用UPrimitiveComponent::UnregisterComponent()再DestroyComponent() - 若使用
UInstancedStaticMeshComponent,绝不能对单个实例调用卸载——必须整批ClearInstances(),否则FInstancedStaticMeshInstanceData指针悬空
视野裁剪逻辑里最容易漏掉的三个检查点
很多团队写了 Frustum 检测,但卸载后仍卡顿或穿模,往往栽在这几个非显性依赖上:
-
UWorld::LineTraceSingleByChannel()仍可能命中已隐藏但未卸载的碰撞体——需同步调用UPrimitiveComponent::SetCollisionEnabled(ECollisionEnabled::NoCollision) - 玩家角色的
UCapsuleComponent若启用了bGenerateOverlapEvents,而视野外 Actor 的碰撞体只是隐藏没关事件,仍会触发OnComponentBeginOverlap回调(哪怕不可见) - PostProcessVolume、AudioVolume 等非渲染组件也受 WorldPartition 加载影响;若它们附着在被卸载的 Actor 上,会导致体积效果突变,需单独管理其激活状态
真正难的不是“怎么删”,而是删完之后,谁还在悄悄引用它、谁还在等它、谁以为它还活着。这些隐式依赖比代码逻辑更耗调试时间。


















