diff --git a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp index e1a0891f..bc72b4f9 100644 --- a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp +++ b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp @@ -298,6 +298,22 @@ namespace { } } + // JB: We skip serialization of RelativeLocation for root components. + // JB: The root component relative location is redundant as it equals to the actor location. + // JB: Having it set during deserialization causes problems with PhysicX when a large number of prefabs are spawned at runtime. + // JB: Restoring the relative location will essentially put all spawned actors on top of each other even if the prefabs are spawned at different places. + // JB: In such a case, the physics has to deal with the overlaps (or at least I think so) and significantly slows down. + // JB: Especially if the assets have a large number of collisions. + USceneComponent* SceneComponent = Cast(ObjToSerialize); + if(SceneComponent && Cast(SceneComponent->GetAttachParent())) + { + if (Property->GetName().Equals("RelativeLocation")) + { + return true; + } + } + + return false; } @@ -343,6 +359,7 @@ namespace { } UPrefabricatorAsset* PrefabAsset = Cast(PrefabActor->PrefabComponent->PrefabAssetInterface.LoadSynchronous()); + USceneComponent* SceneComponent = Cast(ObjToSerialize); if (!PrefabAsset) { return; @@ -515,12 +532,28 @@ void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData for (const FPrefabricatorComponentData& ComponentData : InActorData.Components) { if (UActorComponent** SearchResult = ComponentsByName.Find(ComponentData.ComponentName)) { UActorComponent* Component = *SearchResult; + USceneComponent* SceneComponent = Cast(Component); + + // JB: We store the world location in case we would be restoring a component simulating physics. + // JB: This is necessary only for prefab spawned at runtime. + FVector WorldLocation = FVector::ZeroVector; + if (InActor->HasActorBegunPlay()) { + if (SceneComponent) + { + WorldLocation = SceneComponent->GetComponentLocation(); + } + } + bool bPreviouslyRegister; { //SCOPE_CYCLE_COUNTER(STAT_LoadStateFromPrefabAsset_UnregisterComponent); bPreviouslyRegister = Component->IsRegistered(); if (InSettings.bUnregisterComponentsBeforeLoading && bPreviouslyRegister) { Component->UnregisterComponent(); + // JB: Some of the components (e.g., UPhysicsConstraintComponent) also require re-initialization. + if (Component->HasBeenInitialized()) { + Component->UninitializeComponent(); + } } } @@ -532,7 +565,14 @@ void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData { //SCOPE_CYCLE_COUNTER(STAT_LoadStateFromPrefabAsset_RegisterComponent); if (InSettings.bUnregisterComponentsBeforeLoading && bPreviouslyRegister) { + // JB: Register component will also initialize component if necessary. Component->RegisterComponent(); + // JB: Components that are simulating physics are detached from the actor on register. + // JB: Restoring their relative location above will cause them to be spawned at a wrong location so we fix it. + // JB: This is necessary only for prefab spawned at runtime. + if (InActor->HasActorBegunPlay() && SceneComponent->IsSimulatingPhysics()) { + SceneComponent->SetRelativeLocation(WorldLocation); + } } } } @@ -679,7 +719,9 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } } - ChildActor = Service->SpawnActor(ActorClass, FTransform::Identity, PrefabActor->GetLevel(), Template); + //JB: Spawning actors on top of each other may cause problems with PhysicX (as it needs to compute the overlaps). + FTransform WorldTransform = ActorItemData.RelativeTransform * PrefabActor->GetTransform(); + ChildActor = Service->SpawnActor(ActorClass, WorldTransform, PrefabActor->GetLevel(), Template); if (!Template) { LoadActorState(ChildActor, ActorItemData, InSettings); if (InState.IsValid()) {