|
| 1 | + |
| 2 | +--- |
| 3 | +title: "UE5 | ChildActorComponent Transient Child在PIE下消失的问题" |
| 4 | +date: 2024-08-15T22:49:58+08:00 |
| 5 | +draft: false |
| 6 | +categories: [ "UE"] |
| 7 | +isCJKLanguage: true |
| 8 | +slug: "e488f1cb" |
| 9 | +toc: true |
| 10 | +mermaid: false |
| 11 | +fancybox: false |
| 12 | +blueprint: false |
| 13 | +# latex support |
| 14 | +# katex: true |
| 15 | +# markup: mmark |
| 16 | +# mmarktoc: false |
| 17 | +UEVersion: 5.3.2 |
| 18 | +--- |
| 19 | + |
| 20 | + |
| 21 | +# To be Transient or Not To be? |
| 22 | + |
| 23 | + |
| 24 | +```cpp |
| 25 | +/** |
| 26 | + * Should the spawned actor be marked as transient? |
| 27 | + * @note The spawned actor will also be marked transient if this component or its owner actor are transient, regardless of the state of this flag. |
| 28 | + */ |
| 29 | +UPROPERTY(EditDefaultsOnly, Category=ChildActorComponent) |
| 30 | +uint8 bChildActorIsTransient:1; |
| 31 | +``` |
| 32 | +
|
| 33 | +这个Flag主要作用是给Spawn出来的ChildActor加上Transient标记。 |
| 34 | +
|
| 35 | +带上Transient标记,所有序列化和SavePackage之类的函数都会跳过这个ChildActor。 |
| 36 | +适合ChildActor可能会进行一些会导致PackageDirty的情况。 |
| 37 | +
|
| 38 | +
|
| 39 | +# Bug: PIE下ChildActor消失? |
| 40 | +
|
| 41 | +如果带上Transient,摆在场景上的ChildActorComponent在PIE下没法看见创建的ChildActor. |
| 42 | +
|
| 43 | +
|
| 44 | + |
| 45 | +
|
| 46 | +
|
| 47 | +|Editor|PIE| |
| 48 | +|-|-| |
| 49 | +|| |
| 50 | +| |
| 51 | +
|
| 52 | +
|
| 53 | +
|
| 54 | +仔细分析了一波原因,最后定位到在`ULevel::Serialize`里: |
| 55 | +
|
| 56 | +对于带有`Transient`标记的Actor不会被序列化到`Actors`数组里。 |
| 57 | +
|
| 58 | +```cpp |
| 59 | + else if (Ar.IsSaving() && Ar.IsPersistent()) |
| 60 | + { |
| 61 | + UPackage* LevelPackage = GetOutermost(); |
| 62 | + TArray<AActor*> EmbeddedActors; |
| 63 | + EmbeddedActors.Reserve(Actors.Num()); |
| 64 | +
|
| 65 | + Algo::CopyIf(Actors, EmbeddedActors, [&](AActor* Actor) |
| 66 | + { |
| 67 | + if (!Actor) |
| 68 | + { |
| 69 | + return false; |
| 70 | + } |
| 71 | +
|
| 72 | + check(Actor->GetLevel() == this); |
| 73 | +
|
| 74 | + if (Actor->HasAnyFlags(RF_Transient)) |
| 75 | + { |
| 76 | + return false; |
| 77 | + } |
| 78 | +
|
| 79 | +``` |
| 80 | + |
| 81 | + |
| 82 | +当从Editor进入到PIE时候,会进行`UWorld`的复制,`ULevel`的重新初始化,最后进入到 |
| 83 | + |
| 84 | +```cpp |
| 85 | +bool ULevel::IncrementalRegisterComponents(bool bPreRegisterComponents, int32 NumComponentsToUpdate, FRegisterComponentContext* Context) |
| 86 | +{ |
| 87 | + // Find next valid actor to process components registration |
| 88 | + |
| 89 | + if (OwningWorld) |
| 90 | + { |
| 91 | + OwningWorld->SetAllowDeferredPhysicsStateCreation(true); |
| 92 | + } |
| 93 | + |
| 94 | + while (CurrentActorIndexForIncrementalUpdate < Actors.Num()) |
| 95 | + { |
| 96 | + .... |
| 97 | + Actor->IncrementalRegisterComponents(NumComponentsToUpdate, Context); |
| 98 | + } |
| 99 | +``` |
| 100 | +
|
| 101 | +
|
| 102 | +由于`Editor`的`ChildActor`没有被序列化到`ULevels Actor`里,新建的PIE UWorld的Level将不会有这个ChildActor,所以这里会跳过Child Actor的Register。 |
| 103 | +
|
| 104 | +所以,`ChildActor`虽然存在在PIE World(由UWorld的复制的时候带过来的,所以能正常执行BeginPlay / TickFunction),但是由于没有注册所以没有创建`RenderState`和`PhysicsState`,所以在渲染时看不到,物理上也无法交互。 |
| 105 | +
|
| 106 | +一个简单的修复就是在 `ChildActorComponent`的`OnRegister`中,如果发现`ChildActor`没注册,重新注册。 |
| 107 | +
|
| 108 | +
|
| 109 | +
|
| 110 | +``` |
| 111 | +diff --git a/Engine/Source/Runtime/Engine/Private/Components/ChildActorComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/ChildActorComponent.cpp |
| 112 | +--- a/Engine/Source/Runtime/Engine/Private/Components/ChildActorComponent.cpp |
| 113 | ++++ b/Engine/Source/Runtime/Engine/Private/Components/ChildActorComponent.cpp |
| 114 | + |
| 115 | +@@ -102,6 +102,13 @@ |
| 116 | +void UChildActorComponent::OnRegister() |
| 117 | +{ |
| 118 | + Super::OnRegister(); |
| 119 | + |
| 120 | + .... |
| 121 | + { |
| 122 | + CreateChildActor(); |
| 123 | + } |
| 124 | ++ // register all components if necessary |
| 125 | ++ if(ChildActor && !ChildActor->HasActorRegisteredAllComponents()) |
| 126 | ++ { |
| 127 | ++ ChildActor->RegisterAllComponents(); |
| 128 | ++ } |
| 129 | + } |
| 130 | + |
| 131 | + void UChildActorComponent::Serialize(FArchive& Ar) |
| 132 | + |
| 133 | +``` |
0 commit comments