void FBlueprintCompileReinstancer::ReinstanceFast() { UE_LOG(LogBlueprint, Log, TEXT("BlueprintCompileReinstancer: Doing a fast path refresh on class '%s'."), *GetPathNameSafe(ClassToReinstance)); TArray<UObject*> ObjectsToReplace; GetObjectsOfClass(DuplicatedClass, ObjectsToReplace, /*bIncludeDerivedClasses=*/ false); const bool bIsActor = ClassToReinstance->IsChildOf<AActor>(); const bool bIsAnimInstance = ClassToReinstance->IsChildOf<UAnimInstance>(); const bool bIsComponent = ClassToReinstance->IsChildOf<UActorComponent>(); for (auto Obj : ObjectsToReplace) { UE_LOG(LogBlueprint, Log, TEXT(" Fast path is refreshing (not replacing) %s"), *Obj->GetFullName()); if ((!Obj->IsTemplate() || bIsComponent) && !Obj->IsPendingKill()) { const bool bIsSelected = bIsActor ? Obj->IsSelected() : false; Obj->SetClass(ClassToReinstance); if (bIsActor) { auto Actor = CastChecked<AActor>(Obj); Actor->ReregisterAllComponents(); Actor->RerunConstructionScripts(); if (bIsSelected) { GEditor->SelectActor(Actor, /*bInSelected =*/true, /*bNotify =*/true, false, true); } } if (bIsAnimInstance) { // Initialising the anim instance isn't enough to correctly set up the skeletal mesh again in a // paused world, need to initialise the skeletal mesh component that contains the anim instance. if (USkeletalMeshComponent* SkelComponent = Cast<USkeletalMeshComponent>(Obj->GetOuter())) { SkelComponent->InitAnim(true); } } } } TArray<UObject*> SourceObjects; TMap<UObject*, UObject*> OldToNewInstanceMap; TMap<FStringAssetReference, UObject*> ReinstancedObjectsWeakReferenceMap; FReplaceReferenceHelper::IncludeCDO(DuplicatedClass, ClassToReinstance, OldToNewInstanceMap, SourceObjects, OriginalCDO); FReplaceReferenceHelper::FindAndReplaceReferences(SourceObjects, &ObjectsThatShouldUseOldStuff, ObjectsToReplace, OldToNewInstanceMap, ReinstancedObjectsWeakReferenceMap); }
void AActor::PostEditMove(bool bFinished) { if ( ReregisterComponentsWhenModified() ) { UBlueprint* Blueprint = Cast<UBlueprint>(GetClass()->ClassGeneratedBy); if(Blueprint && (Blueprint->bRunConstructionScriptOnDrag || bFinished) && !FLevelUtils::IsMovingLevel() ) { FNavigationLockContext NavLock(GetWorld(), ENavigationLockReason::AllowUnregister); RerunConstructionScripts(); } } if ( bFinished ) { GetWorld()->bDoDelayedUpdateCullDistanceVolumes = true; GetWorld()->bAreConstraintsDirty = true; FEditorSupportDelegates::RefreshPropertyWindows.Broadcast(); // Let other systems know that an actor was moved GEngine->BroadcastOnActorMoved( this ); FEditorSupportDelegates::UpdateUI.Broadcast(); } // If the root component was not just recreated by the construction script - call PostEditComponentMove on it if(RootComponent != NULL && !RootComponent->IsCreatedByConstructionScript()) { // @TODO Should we call on ALL components? RootComponent->PostEditComponentMove(bFinished); } if (bFinished) { // update actor and all its components in navigation system after finishing move // USceneComponent::UpdateNavigationData works only in game world UNavigationSystem::UpdateNavOctreeBounds(this); TArray<AActor*> ParentedActors; GetAttachedActors(ParentedActors); for (int32 Idx = 0; Idx < ParentedActors.Num(); Idx++) { UNavigationSystem::UpdateNavOctreeBounds(ParentedActors[Idx]); } // not doing manual update of all attached actors since UpdateActorAndComponentsInNavOctree should take care of it UNavigationSystem::UpdateActorAndComponentsInNavOctree(*this); } }
void AActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { UProperty* PropertyThatChanged = PropertyChangedEvent.Property; FName PropertyName = PropertyThatChanged != NULL ? PropertyThatChanged->GetFName() : NAME_None; const bool bTransformationChanged = (PropertyName == Name_RelativeLocation || PropertyName == Name_RelativeRotation || PropertyName == Name_RelativeScale3D); // During SIE, allow components to reregistered and reconstructed in PostEditChangeProperty. // This is essential as construction is deferred during spawning / duplication when in SIE. if ((GEditor && GEditor->bIsSimulatingInEditor) || ReregisterComponentsWhenModified()) { // In the Undo case we have an annotation storing information about constructed components and we do not want // to improperly apply out of date changes so we need to skip registration of all blueprint created components // and defer instance components attached to them until after rerun if (CurrentTransactionAnnotation.IsValid()) { UnregisterAllComponents(); TInlineComponentArray<UActorComponent*> Components; GetComponents(Components); Components.Sort([](UActorComponent& A, UActorComponent& B) { if (&B == B.GetOwner()->GetRootComponent()) { return false; } if (USceneComponent* ASC = Cast<USceneComponent>(&A)) { if (ASC->GetAttachParent() == &B) { return false; } } return true; }); bool bRequiresReregister = false; for (UActorComponent* Component : Components) { if (Component->CreationMethod == EComponentCreationMethod::Native) { Component->RegisterComponent(); } else if (Component->CreationMethod == EComponentCreationMethod::Instance) { USceneComponent* SC = Cast<USceneComponent>(Component); if (SC == nullptr || SC == RootComponent || (SC->GetAttachParent() && SC->GetAttachParent()->IsRegistered())) { Component->RegisterComponent(); } else { bRequiresReregister = true; } } else { bRequiresReregister = true; } } RerunConstructionScripts(); if (bRequiresReregister) { ReregisterAllComponents(); } } else { UnregisterAllComponents(); RerunConstructionScripts(); ReregisterAllComponents(); } } // Let other systems know that an actor was moved if (bTransformationChanged) { GEngine->BroadcastOnActorMoved( this ); } if (GetWorld()) { GetWorld()->bDoDelayedUpdateCullDistanceVolumes = true; } FEditorSupportDelegates::UpdateUI.Broadcast(); Super::PostEditChangeProperty(PropertyChangedEvent); }