Unreal Engine 5调试资源依赖来源的方法
有时候通过关系查看器只能看到产生了依赖,并不能找到具体是哪里产生的依赖,所以这时候不可避免的需要通过调试源代码来获取相关信息。
一些比较好的切入点记录如下:
首先先确认依赖是否存在,先在这里下个断点,然后尝试保存目标资源:
ESavePackageResult InnerSave(FSaveContext& SaveContext) { // Create slow task dialog if needed const int32 TotalSaveSteps = 3; FScopedSlowTask SlowTask(TotalSaveSteps, FText(), SaveContext.IsUsingSlowTask()); SlowTask.MakeDialogDelayed(3.0f, SaveContext.IsFromAutoSave()); // Harvest Package SlowTask.EnterProgressFrame(); SaveContext.GetObjectSaveContext().ObjectSaveContextPhase = EObjectSaveContextPhase ::Harvest; SaveContext.Result = HarvestPackage(SaveContext); if (SaveContext.Result != ESavePackageResult::Success) // 这里,通过检查SaveContext的HarvestedRealms数组中元素的Imports数组来确认依赖是否存存在,如果存在就按照下面的步骤继续定位。 { return SaveContext.Result; } }
void FPackageHarvester::TryHarvestImport(TObjectPtr<UObject> InObject, UE::SavePackageUtilities::FObjectStatus& ObjectStatus) { UE_COOK_RESULTPROJECTION_SCOPED(UE::Cook::ResultProjection::PackageAndClass); // Those should have been already validated check(InObject && !ObjectStatus.IsInSavePackage(InObject, SaveContext.GetPackage())) if (InObject == nullptr) { return; } // Do not add unsaveable imports to any realm if (SaveContext.IsUnsaveable(InObject, ObjectStatus)) { return; } // Do not add imports in packages excluded from cooking to any realm if (FCoreUObjectDelegates::ShouldCookPackageForPlatform.IsBound()) { if (!FCoreUObjectDelegates::ShouldCookPackageForPlatform.Execute(InObject.GetPackage(), CookingTarget())) { return; } } // Filter out any realms in which the import is excluded FHarvestScope NotExcludedScope = EnterNotExcludedScope(InObject, ObjectStatus); if (NotExcludedScope.IsEmpty()) { return; } HarvestImport(InObject); // 这个位置,就是将目标对象的基本信息写入到文件头的Imports区块,所以在这里下断点是一个不错的主意 ProcessImport(InObject, ObjectStatus); }
如果下断点调试很麻烦的话,也可以直接加个这个调试代码,用完记得删除即可:
if (InObject && InObject->GetName() == TEXT("TilingCurl32")) { UE_LOG(LogTemp, Warning, TEXT("TilingCurl32")); }
ESavePackageResult HarvestPackage(FSaveContext& SaveContext) { SCOPED_SAVETIMER(UPackage_Save_HarvestPackage); FPackageHarvester Harvester(SaveContext); EObjectFlags TopLevelFlags = SaveContext.GetTopLevelFlags(); UObject* Asset = SaveContext.GetAsset(); auto TryHarvestRootObject = [&Harvester, &SaveContext](UObject* InRoot) { // ForEachObjectWithPackage will filter out Transient objects but not objects with the bForceTransient Save Override // Perform one more check to cover this case UE::SavePackageUtilities::FObjectStatus& ObjectStatus = SaveContext.GetCachedObjectStatus(InRoot); if (SaveContext.IsTransient(InRoot, ObjectStatus)) { return; } Harvester.TryHarvestExport(InRoot, ObjectStatus); // if we are automatically generating an optional package output, re-harvest objects with that realm as the default if (SaveContext.IsSaveAutoOptional()) { //@todo: FH: the top level might have to be altered to use the editor one when auto harvesting optional data FSaveContext::FSetSaveRealmToSaveScope RealmScope(SaveContext, ESaveRealm::Optional); Harvester.TryHarvestExport(InRoot, ObjectStatus); } }; // if no top level flags are passed, only use the provided package asset as root if (TopLevelFlags == RF_NoFlags) { TryHarvestRootObject(Asset); } // Otherwise use all objects which have the relevant flags else { ForEachObjectWithPackage(SaveContext.GetPackage(), [&TryHarvestRootObject, TopLevelFlags](UObject* InObject) { if (InObject->HasAnyFlags(TopLevelFlags)) { TryHarvestRootObject(InObject); } return true; }, true/*bIncludeNestedObjects */, RF_Transient); } #if WITH_VERSE_VM || defined(__INTELLISENSE__) Verse::FAllocationContext Context = Verse::FAllocationContextPromise{}; if (Verse::VPackage* VersePackage = Verse::GlobalProgram->LookupPackage(Context, SaveContext.GetPackage())) { Harvester.TryHarvestCellExport(VersePackage); } #endif // Now process harvested roots
// 下面这个位置也不错,你可以通过断点端下来,然后查看Harvester的属性ExportsToProcess,这是一个链表,通过不停展开节点可以看到几乎所有有关的对象列表
while (FPackageHarvester::FExportWithContext ExportContext = Harvester.PopExportToProcess()) { #if WITH_VERSE_VM || defined(__INTELLISENSE__) if (ExportContext.Export) { Harvester.ProcessExport(ExportContext); } else if (ExportContext.CellExport) { Harvester.ProcessCellExport(ExportContext); } #else Harvester.ProcessExport(ExportContext); #endif } // If we have a valid optional context and we are saving it, // transform any harvested non optional export into imports // Mark other optional import package as well if (!SaveContext.IsSaveAutoOptional() && SaveContext.IsSaveOptional() && SaveContext.IsCooking() && SaveContext.GetHarvestedRealm(ESaveRealm::Optional).GetExports().Num() && SaveContext.GetHarvestedRealm(ESaveRealm::Game).GetExports().Num()) { bool bHasNonOptionalSelfReference = false; FHarvestedRealm& OptionalContext = SaveContext.GetHarvestedRealm(ESaveRealm::Optional); for (auto It = OptionalContext.GetExports().CreateIterator(); It; ++It) { if (!FPackageHarvester::ShouldObjectBeHarvestedInOptionalRealm(It->Obj, SaveContext)) { // Make sure the export is found in the game context as well if (FTaggedExport* GameExport = SaveContext.GetHarvestedRealm(ESaveRealm::Game).GetExports().Find(It->Obj)) { // Flagging the export in the game context to generate it's public hash isn't necessary anymore //GameExport->bGeneratePublicHash = true; // Transform the export as an import OptionalContext.AddImport(It->Obj); // Flag the package itself to be an import bHasNonOptionalSelfReference = true; } // if not found in the game context and the reference directly came from an optional object, record an illegal reference else if (It->bFromOptionalReference) { SaveContext.RecordIllegalReference(nullptr, It->Obj, EIllegalRefReason::ReferenceFromOptionalToMissingGameExport); } It.RemoveCurrent(); } } // Also add the current package itself as an import if we are referencing any non optional export if (bHasNonOptionalSelfReference) { OptionalContext.AddImport(SaveContext.GetPackage()); } } { FPackageHarvester::FHarvestScope RootReferenceScope = Harvester.EnterRootReferencesScope(); // Trim PrestreamPackage list TSet<TObjectPtr<UPackage>>& PrestreamPackages = SaveContext.GetPrestreamPackages(); TSet<TObjectPtr<UPackage>> KeptPrestreamPackages; for (TObjectPtr<UPackage> Pkg : PrestreamPackages) { // If the prestream package hasn't been otherwise already marked as an import, keep it as such and mark it as an import if (!SaveContext.IsImport(Pkg)) { KeptPrestreamPackages.Add(Pkg); Harvester << Pkg; } } Exchange(PrestreamPackages, KeptPrestreamPackages); // Harvest the PrestreamPackage class name if needed if (PrestreamPackages.Num() > 0) { Harvester.HarvestPackageHeaderName(UE::SavePackageUtilities::NAME_PrestreamPackage); } #if WITH_METADATA // Harvest package MetaData if (!SaveContext.IsCooking()) { FMetaData& PackageMetaData = SaveContext.GetPackage()->GetMetaData(); Harvester << PackageMetaData; } #endif // if we have a WorldTileInfo, we need to harvest its dependencies as well, i.e. Custom Version if (FWorldTileInfo* WorldTileInfo = SaveContext.GetPackage()->GetWorldTileInfo()) { Harvester << *WorldTileInfo; } } // The Editor version is used as part of the check to see if a package is too old to use the gather cache, so we always have to add it if we have gathered loc for this asset // We need to set the editor custom version before we copy the version container to the summary, otherwise we may end up with corrupt assets // because we later do it on the Linker when actually gathering loc data if (!SaveContext.IsFilterEditorOnly()) { Harvester.UsingCustomVersion(FEditorObjectVersion::GUID); } SaveContext.SetCustomVersions(Harvester.GetCustomVersions()); SaveContext.SetTransientPropertyOverrides(Harvester.ReleaseTransientPropertyOverrides()); if (SaveContext.IsCooking()) { SaveContext.AddExportedClassesToDependencies(); } // Contractually all BuildResultDependencies (which come from PreSave or from Serialize) must be // declared by this point. Copy PackageBuildDependencies from those lists and implement our contract that // PackageBuildDependencies are in the name map. SaveContext.UpdateEditorRealmPackageBuildDependencies(); return ReturnSuccessOrCancel(); }
比如我在查看资源/Game/Effects/Particles/Weapons/NS_WeaponFire_MuzzleFlash_Rifle.NS_WeaponFire_MuzzleFlash_Rifle的时候发现一个依赖:/Niagara/VectorFields/Assets/TilingCurl32。
直观上不好找到这个依赖是如何产生的,我在第一步的位置打了日志,发现这个引用是由于下面这个类的Field属性产生的:
class UNiagaraDataInterfaceVectorField : public UNiagaraDataInterface { GENERATED_UCLASS_BODY() BEGIN_SHADER_PARAMETER_STRUCT(FShaderParameters, ) SHADER_PARAMETER(FVector3f, TilingAxes) SHADER_PARAMETER(FVector3f, Dimensions) SHADER_PARAMETER(FVector3f, MinBounds) SHADER_PARAMETER(FVector3f, MaxBounds) SHADER_PARAMETER_TEXTURE(Texture3D, Texture) SHADER_PARAMETER_SAMPLER(SamplerState, Sampler) END_SHADER_PARAMETER_STRUCT(); public: /** Vector field to sample from. */ UPROPERTY(EditAnywhere, Category = VectorField) TObjectPtr<UVectorField> Field; // 省略掉一些代码 }
所以我就需要去ExportsToProcess中找这个类的对象,在调试器中,我们不停展开NextNode就可以看到下面的数据:
Harvester = {FPackageHarvester} {SaveContext={Result=Success, GatherableTextResultFlags=Empty, PackageSavedHash=0000000000000000000000000000000000000000, ...}, ExportsToProcess={Head=0x0000013cf4810a20 {NextNode=0x0000000000000000 {NextNode=???, Item={Export=???, CellExport=...}}, ...}, ...}, ...}
UE::SavePackageUtilities::Private::FArchiveSavePackageCollector = {UE::SavePackageUtilities::Private::FArchiveSavePackageCollector}
SaveContext = {FSaveContext &} {Result=Success, GatherableTextResultFlags=Empty, PackageSavedHash=0000000000000000000000000000000000000000, ...}
ExportsToProcess = {TQueue<FPackageHarvester::FExportWithContext, 1>} {Head=0x0000013cf4810a20 {NextNode=0x0000000000000000 {NextNode=???, Item={Export=???, CellExport=???, HarvestedFromRealms=Invalid}}, Item={Export=0x0000013cd80bf980 (Name="ParameterHierarchyRoot", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , ...}}, ...}
Head = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4810a20 {NextNode=0x0000000000000000 {NextNode=???, Item={Export=???, CellExport=???, HarvestedFromRealms=Invalid}}, Item={Export=0x0000013cd80bf980 (Name="ParameterHierarchyRoot", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , ...}}
Tail = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *} 0x0000013cf4816b70 {NextNode=0x0000013cf4819270 {NextNode=0x0000013cf4819630 {NextNode=0x0000013cf4819660 {NextNode=0x0000013cf4813d20 {NextNode=0x0000013cf481ac80 {NextNode=0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4819270 {NextNode=0x0000013cf4819630 {NextNode=0x0000013cf4819660 {NextNode=0x0000013cf4813d20 {NextNode=0x0000013cf481ac80 {NextNode=0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4819630 {NextNode=0x0000013cf4819660 {NextNode=0x0000013cf4813d20 {NextNode=0x0000013cf481ac80 {NextNode=0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4819660 {NextNode=0x0000013cf4813d20 {NextNode=0x0000013cf481ac80 {NextNode=0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4813d20 {NextNode=0x0000013cf481ac80 {NextNode=0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf481ac80 {NextNode=0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4819cc0 {NextNode=0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf48149b0 {NextNode=0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4810030 {NextNode=0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf48158b0 {NextNode=0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf481eb50 {NextNode=0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf48164e0 {NextNode=0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf481a140 {NextNode=0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf48165d0 {NextNode=0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4814650 {NextNode=0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf481f8d0 {NextNode=0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=0x0000013cf4817680 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4815af0 {NextNode=0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=0x0000013cf4817680 {NextNode=0x0000013cf4816750 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf481c150 {NextNode=0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=0x0000013cf4817680 {NextNode=0x0000013cf4816750 {NextNode=0x0000013cf4812d60 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf48117a0 {NextNode=0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=0x0000013cf4817680 {NextNode=0x0000013cf4816750 {NextNode=0x0000013cf4812d60 {NextNode=0x0000013cf4812580 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4812250 {NextNode=0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=0x0000013cf4817680 {NextNode=0x0000013cf4816750 {NextNode=0x0000013cf4812d60 {NextNode=0x0000013cf4812580 {NextNode=0x0000013cf481a9b0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
NextNode = {TQueue<FPackageHarvester::FExportWithContext, 1>::TNode *volatile} 0x0000013cf4813240 {NextNode=0x0000013cf48102a0 {NextNode=0x0000013cf4818010 {NextNode=0x0000013cf4817680 {NextNode=0x0000013cf4816750 {NextNode=0x0000013cf4812d60 {NextNode=0x0000013cf4812580 {NextNode=0x0000013cf481a9b0 {NextNode=0x0000013cf48138d0 {NextNode=...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}, ...}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013d745e9b40 (Name="NiagaraDataInterfaceVectorField"_5, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Export = {UNiagaraDataInterfaceVectorField *} 0x0000013d745e9b40 (Name="NiagaraDataInterfaceVectorField"_5, InternalFlags=ReachabilityFlag0)
UNiagaraDataInterface = {UNiagaraDataInterface} (Name="NiagaraDataInterfaceVectorField"_5, InternalFlags=ReachabilityFlag0)
Field = {TObjectPtr<UVectorField>} {={ObjectPtr=0x0000013cf840fc00 (Name="TilingCurl32", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013cf840fc00 (Name="TilingCurl32", InternalFlags=ReachabilityFlag0)}}
bTileX = {bool} true
bTileY = {bool} true
bTileZ = {bool} true
CellExport = {Verse::VCell *} NULL
HarvestedFromRealms = {TArray<enum ESaveRealm, TSizedInlineAllocator<2, 32, TSizedDefaultAllocator<32>>>} Num=1, Max=2
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cf33c0c00 (Name="Texture2D"_1, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd9091140 (Name="NiagaraEditorParametersAdapter"_0, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd793d100 (Name="NiagaraEmitterEditorData"_0, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd914c800 (Name="GPUComputeScript", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd92a5000 (Name="NiagaraSpriteRendererProperties"_0, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd940e680 (Name="NiagaraScratchPadContainer"_1, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd940e6c0 (Name="NiagaraScratchPadContainer"_0, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd80bee80 (Name="NiagaraScriptSource"_0, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd914b400 (Name="EmitterUpdateScript", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd914a000 (Name="EmitterSpawnScript", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd914dc00 (Name="SpawnScript", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd9150000 (Name="UpdateScript", InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013cd8fa8880 (Name="NiagaraGraph"_0, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013da62ba200 (Name="NiagaraDataInterfaceColorCurve"_5, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013da637b700 (Name="NiagaraDataInterfaceVectorField"_5, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013da62bc000 (Name="NiagaraDataInterfaceColorCurve"_5, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013d744ab640 (Name="NiagaraDataInterfaceVectorField"_5, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000013da62bc000 (Name="NiagaraDataInterfaceColorCurve"_5, InternalFlags=ReachabilityFlag0), CellExport=0x0000000000000000 , HarvestedFromRealms=Num=1, Max=2}
Item = {FPackageHarvester::FExportWithContext} {Export=0x0000000000000000 (Name=???, InternalFlags=???), CellExport=0x0000000000000000 , HarvestedFromRealms=Empty, Max=2}
CurrentExportDependencies = {FPackageHarvester::FExportDependencies} {CurrentExport=0x0000013d744ab640 (Name="NiagaraDataInterfaceVectorField"_5, InternalFlags=ReachabilityFlag0), CurrentCellExport=0x0000000000000000 , ObjectReferences=Empty, ...}
TransientPropertyOverrides = {TMap<UObject *, TSet<FProperty *, DefaultKeyFuncs<FProperty *, 0>, FDefaultSetAllocator>, FDefaultSetAllocator, TDefaultMapHashableKeyFuncs<UObject *, TSet<FProperty *, DefaultKeyFuncs<FProperty *, 0>, FDefaultSetAllocator>, 0>>} Empty
CurrentExportHarvestingRealms = {TArray<enum ESaveRealm, TSizedInlineAllocator<2, 32, TSizedDefaultAllocator<32>>>} Num=1, Max=2
通过这个链表信息就可以大概知道引用路径是如何产生的了。除此之外,还可以直接在这个位置下断点:
void FPackageHarvester::HarvestExport(UObject* InObject) { bool bFromOptionalRef = CurrentExportDependencies.CurrentExport && CurrentExportDependencies.CurrentExport->GetClass()->HasAnyClassFlags(CLASS_Optional); FTaggedExport TaggedExport(InObject, !DoesObjectNeedLoadForEditorGame(InObject), bFromOptionalRef); ForEachExportHarvestingRealm([this, &TaggedExport, InObject](ESaveRealm HarvestingRealm) { FHarvestedRealm& RealmData = SaveContext.GetHarvestedRealm(HarvestingRealm); RealmData.AddExport(TaggedExport); RealmData.GetNamesReferencedFromPackageHeader().Add(InObject->GetFName().GetDisplayIndex()); });
// 在下面这里下断点可以看到将所有要处理的对象添加进去的过程,所以对于上面的需求我只需要关注什么时候放的NiagaraDataInterfaceVectorField即可。
ExportsToProcess.Enqueue({ InObject, nullptr, CurrentExportHarvestingRealms });
}
断到我们想要的对象后,再通过调用堆栈去顺藤摸瓜也是可以的。也许你可能不会一次性就摸清楚整个流程,所以需要每次往上走几步,然后重复上面的操作,最后再把整个过程组合起来就可以了。
对于我上面的case,第一步摸到了这个流程:
UNiagaraScript(UpdateScript_1) =>
TArray<FNiagaraScriptDataInterfaceInfo> CachedDefaultDataInterfaces; =>
TObjectPtr<class UNiagaraDataInterface> DataInterface; // 这里持有的UNiagaraDataInterfaceVectorField
所以我需要找到的就是UNiagaraScript(UpdateScript_1)这个对象是怎么来的。放开断点后再次保存资产,又会进入到这个位置,这次注意观察UpdateScript_1的出现,然后断下来后再次顺腾摸瓜得到第二波信息。
this = {FPackageHarvester *const} 0x00000033e335b120 {SaveContext={Result=Success, GatherableTextResultFlags=Empty, PackageSavedHash=0000000000000000000000000000000000000000, ...}, ExportsToProcess={Head=0x0000013cf48110b0 {NextNode=0x0000000000000000 {NextNode=???, Item=...}, ...}, ...}, ...} InObject = {UNiagaraScript *} 0x0000013cd90c3c00 (Name="UpdateScript"_1, InternalFlags=ReachabilityFlag0) UNiagaraScriptBase = {UNiagaraScriptBase} (Name="UpdateScript"_1, InternalFlags=ReachabilityFlag0) ValidationRules = {TArray<TObjectPtr<UNiagaraValidationRule>, TSizedDefaultAllocator<32>>} Empty Usage = {ENiagaraScriptUsage} ParticleUpdateScript UsageId = {FGuid} {00000000-0000-0000-0000-000000000000} ExposedVersion = {FGuid} {A7F5F47C-4C43-D9CC-BA6C-3E965F6440EA} bVersioningEnabled = {bool} false VersionData = {TArray<FVersionedNiagaraScriptData, TSizedDefaultAllocator<32>>} Num=1, Max=1 VersionedScriptAdapters = {TArray<FVersionedNiagaraScript, TSizedDefaultAllocator<32>>} Num=1, Max=1 RapidIterationParameters = {FNiagaraParameterStore} {Owner=0x0000013cd90c3c00 (Name="UpdateScript"_1, InternalFlags=ReachabilityFlag0), ParameterOffsets=Empty, SortedParameterOffsets=Num=18, Max=18, ...} RapidIterationParametersCookedEditorCache = {FNiagaraParameterStore} {Owner=nullptr, ParameterOffsets=Empty, SortedParameterOffsets=Empty, ...} VersionToOpenInEditor = {FGuid} {00000000-0000-0000-0000-000000000000} UsageIndex_DEPRECATED = {int} 0 ModuleUsageBitmask_DEPRECATED = {int} 248 Category_DEPRECATED = {FText} Empty ProvidedDependencies_DEPRECATED = {TArray<FName, TSizedDefaultAllocator<32>>} Empty RequiredDependencies_DEPRECATED = {TArray<FNiagaraModuleDependency, TSizedDefaultAllocator<32>>} Empty bDeprecated_DEPRECATED = {unsigned int:1} 0 DeprecationMessage_DEPRECATED = {FText} Empty DeprecationRecommendation_DEPRECATED = {TObjectPtr<UNiagaraScript>} nullptr ConversionUtility_DEPRECATED = {TSubclassOf<UNiagaraConvertInPlaceUtilityBase>} {Class=nullptr} bExperimental_DEPRECATED = {unsigned int:1} 0 ExperimentalMessage_DEPRECATED = {FText} Empty NoteMessage_DEPRECATED = {FText} Empty bExposeToLibrary_DEPRECATED = {unsigned int:1} 0 LibraryVisibility_DEPRECATED = {ENiagaraScriptLibraryVisibility} Unexposed NumericOutputTypeSelectionMode_DEPRECATED = {ENiagaraNumericOutputTypeSelectionMode} Largest Description_DEPRECATED = {FText} Empty Keywords_DEPRECATED = {FText} Empty CollapsedViewFormat_DEPRECATED = {FText} Empty ScriptMetaData_DEPRECATED = {TMap<FName, FString, FDefaultSetAllocator, TDefaultMapHashableKeyFuncs<FName, FString, 0>>} Empty Source_DEPRECATED = {TObjectPtr<UNiagaraScriptSourceBase>} nullptr ScriptExecutionParamStoreCPU = {FNiagaraScriptExecutionParameterStore} {ParameterSize=0, bInitialized=0 "'\0'", CachedScriptLiterals=Empty} ScriptExecutionParamStoreGPU = {FNiagaraScriptExecutionParameterStore} {ParameterSize=0, bInitialized=0 "'\0'", CachedScriptLiterals=Empty} ScriptExecutionParamStore = {FNiagaraScriptExecutionParameterStore} {ParameterSize=0, bInitialized=0 "'\0'", CachedScriptLiterals=Empty} ScriptExecutionBoundParameters = {TArray<FNiagaraBoundParameter, TSizedDefaultAllocator<32>>} Empty OnVMScriptCompiledDelegate = {TMulticastDelegate<void __cdecl(UNiagaraScript *, const FGuid &), FDefaultDelegateUserPolicy>} Num=2 OnGPUScriptCompiledDelegate = {TMulticastDelegate<void __cdecl(UNiagaraScript *, const FGuid &), FDefaultDelegateUserPolicy>} Num=2 OnPropertyChangedDelegate = {TMulticastDelegate<void __cdecl(FPropertyChangedEvent &), FDefaultDelegateUserPolicy>} Empty LastReportedVMId = {FNiagaraVMExecutableDataId} {CompilerVersionID={00000000-0000-0000-0000-000000000000}, InterpolatedSpawnMode=NoInterpolation, ScriptUsageTypeID={00000000-0000-0000-0000-000000000000}, ...} CustomAssetRegistryTagCache = {TOptional<TMap<FName, FString, FDefaultSetAllocator, TDefaultMapHashableKeyFuncs<FName, FString, 0>>>} Optional: {Invalid} CachedScriptVMId = {FNiagaraVMExecutableDataId} {CompilerVersionID={03400DA2-912D-05FA-A1D6-DE06A1CFD445}, InterpolatedSpawnMode=NoInterpolation, ScriptUsageTypeID={00000000-0000-0000-0000-000000000000}, ...} ScriptResource = {TSharedPtr<FNiagaraShaderScript, 1>} SharedPtr=0x0000013cd89ffa50 {BaseVMScript=0x0000000000000000 (Name=???, InternalFlags=???), SourceName=Empty, HlslOutput=Empty, ...} LoadedScriptResources = {TArray<FNiagaraShaderScript, TSizedDefaultAllocator<32>>} Empty ScriptResourcesByFeatureLevel = {TSharedPtr<FNiagaraShaderScript, 1> [5]} 0x0000013cd90c4800 {nullptr, nullptr, nullptr, nullptr, nullptr} StatScopesIDs = {TArray<TStatId, TSizedDefaultAllocator<32>>} Num=8, Max=8 CachedScriptResourcesForCooking = {TMap<const ITargetPlatform *, TArray<TUniquePtr<FNiagaraShaderScript, TDefaultDelete<FNiagaraShaderScript>>, TSizedDefaultAllocator<32>>, FDefaultSetAllocator, TDefaultMapHashableKeyFuncs<const ITargetPlatform *, TArray<TUniquePtr<FNiagaraShaderScript, TDefaultDelete<FNiagaraShaderScript>>, TSizedDefaultAllocator<32>>, 0>>} Empty ActiveCompileRoots = {TArray<TObjectPtr<UObject>, TSizedDefaultAllocator<32>>} Empty IsCooked = {bool} false bMigrateParameterDataToHierarchyRoot = {bool} false CachedScriptVM = {FNiagaraVMExecutableData} {ByteCode={Data=Empty, UncompressedSize=-1}, OptimizedByteCode={Data=Empty, UncompressedSize=-1}, OptimizationTask={State=nullptr, Lock={CriticalSection={Opaque1=0x0000013cd90c4908 {0xffffffffffffffff}, Opaque2=0x0000013cd90c4910 {-1, 0}, Opaque3=0x0000013cd90c4918 {...}}}}, ...} CachedParameterCollectionReferences = {TArray<TObjectPtr<UNiagaraParameterCollection>, TSizedDefaultAllocator<32>>} Empty CachedDefaultDataInterfaces = {TArray<FNiagaraScriptDataInterfaceInfo, TSizedDefaultAllocator<32>>} Num=2, Max=2 [0] = {TArray<FNiagaraScriptDataInterfaceInfo, TSizedDefaultAllocator<32>>::ElementType} {DataInterface={={ObjectPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0)}}, Name=...} DataInterface = {TObjectPtr<UNiagaraDataInterface>} {={ObjectPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0)}} = {TObjectPtr<UNiagaraDataInterface>::(anonymous union)} {ObjectPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0)} ObjectPtr = {FObjectPtr} 0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0) UNiagaraDataInterface = {UNiagaraDataInterface} (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0) Field = {TObjectPtr<UVectorField>} {={ObjectPtr=0x0000013cf840fc00 (Name="TilingCurl32", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013cf840fc00 (Name="TilingCurl32", InternalFlags=ReachabilityFlag0)}} = {TObjectPtr<UVectorField>::(anonymous union)} {ObjectPtr=0x0000013cf840fc00 (Name="TilingCurl32", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013cf840fc00 (Name="TilingCurl32", InternalFlags=ReachabilityFlag0)} bTileX = {bool} true bTileY = {bool} true bTileZ = {bool} true Raw View = {FObjectPtr} {={Handle=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0)}} DebugPtr = {UNiagaraDataInterfaceVectorField *} 0x0000013d690e9d80 (Name="NiagaraDataInterfaceVectorField"_13, InternalFlags=ReachabilityFlag0) Name = {FName} "NE_MuzzleFlashSparks.VectorField32" CompileName = {FName} "Emitter.VectorField32" UserPtrIdx = {int} -1 Type = {FNiagaraTypeDefinition} {ClassStructOrEnum={={ObjectPtr=0x0000013b1c480a00 (Name="NiagaraDataInterfaceVectorField", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013b1c480a00 (Name="NiagaraDataInterfaceVectorField", InternalFlags=ReachabilityFlag0)}}, UnderlyingType=...} RegisteredParameterMapRead = {FName} "None" RegisteredParameterMapWrite = {FName} "None" SourceEmitterName = {FString} L"NE_MuzzleFlashSparks" [1] = {TArray<FNiagaraScriptDataInterfaceInfo, TSizedDefaultAllocator<32>>::ElementType} {DataInterface={={ObjectPtr=0x0000013d5dfd3000 (Name="NiagaraDataInterfaceColorCurve"_13, InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013d5dfd3000 (Name="NiagaraDataInterfaceColorCurve"_13, InternalFlags=ReachabilityFlag0)}}, Name="NE_MuzzleFlashSparks.Vector4FromCurve.Vector4Curve", ...} Raw View = {TArray<FNiagaraScriptDataInterfaceInfo, TSizedDefaultAllocator<32>>} {AllocatorInstance={Data=0x0000013d462a4b00 {...}}, ArrayNum=2, ArrayMax=2} ResolvedDataInterfaces = {TArray<FNiagaraScriptResolvedDataInterfaceInfo, TSizedDefaultAllocator<32>>} Num=2, Max=2
USTRUCT() struct FNiagaraEmitterScriptProperties { FNiagaraEmitterScriptProperties() : Script(nullptr) { } GENERATED_BODY() UPROPERTY() TObjectPtr<UNiagaraScript> Script; // 这里 UPROPERTY() TArray<FNiagaraEventReceiverProperties> EventReceivers; UPROPERTY() TArray<FNiagaraEventGeneratorProperties> EventGenerators; NIAGARA_API void InitDataSetAccess(); };
struct FVersionedNiagaraEmitterData { GENERATED_BODY() UPROPERTY() FNiagaraEmitterScriptProperties UpdateScriptProps; // 这里 }
/** * Niagara Emitters are particle spawners that can be reused for different effects by putting them into Niagara systems. * Emitters render their particles using different renderers, such as Sprite Renderers or Mesh Renderers to produce different effects. * * Emitter assets cannot be spawned or used in a level directly, but need to be placed in a Niagara system. Emitters support inheritance, so that * changes to the base asset are automatically picked up by child emitter assets and emitters in system assets. */ UCLASS(MinimalAPI) class UNiagaraEmitter : public UObject, public INiagaraParameterDefinitionsSubscriber, public FNiagaraVersionedObject { GENERATED_UCLASS_BODY() /** Contains all of the versioned emitter data. */ UPROPERTY() TArray<FVersionedNiagaraEmitterData> VersionData; // 这里 }
第二阶段我们继续往上找到了UNiagaraEmitter这个类,它的基本信息是:
this = {UNiagaraEmitter *} 0x0000013cf338c400 (Name="NE_MuzzleFlashSparks", InternalFlags=ReachabilityFlag0) UObject = {UObject} (Name="NE_MuzzleFlashSparks", InternalFlags=ReachabilityFlag0) UObjectBaseUtility = {UObjectBaseUtility} (Name="NE_MuzzleFlashSparks", InternalFlags=ReachabilityFlag0) UObjectBase = {UObjectBase} (Name="NE_MuzzleFlashSparks", InternalFlags=ReachabilityFlag0) ObjectFlags = {EObjectFlags} RF_Transactional | RF_WasLoaded | RF_LoadCompleted InternalIndex = {int} 82160 ClassPrivate = {ObjectPtr_Private::TNonAccessTrackedObjectPtr<UClass>} {ObjectPtr={={ObjectPtr=0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0)}}} ObjectPtr = {TObjectPtr<UClass>} {={ObjectPtr=0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0)}} = {TObjectPtr<UClass>::(anonymous union)} {ObjectPtr=0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0), DebugPtr=0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0)} ObjectPtr = {FObjectPtr} 0x0000013b1c482580 (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0) UStruct = {UStruct} (Name="NiagaraEmitter", InternalFlags=ReachabilityFlag0)

我对Niagara不熟悉,所以只有釜底抽薪选择代码来找,有相关类似需求的朋友可以试试这个方法。当然我们还可以继续按照这个思路往上找,看看NE_MuzzleFlashSparks是怎么来的:最终可以形成这个链条:
UNiagaraSystem(TArray<FNiagaraEmitterHandle> EmitterHandles;) FNiagaraEmitterHandle(FVersionedNiagaraEmitter VersionedInstance;) FVersionedNiagaraEmitter(TObjectPtr<UNiagaraEmitter> Emitter) UNiagaraEmitter(TArray<FVersionedNiagaraEmitterData> VersionData;) FVersionedNiagaraEmitterData(FNiagaraEmitterScriptProperties UpdateScriptProps;) FNiagaraEmitterScriptProperties(TObjectPtr<UNiagaraScript> Script;) UNiagaraScript(TArray<FNiagaraScriptDataInterfaceInfo> CachedDefaultDataInterfaces;) FNiagaraScriptDataInterfaceInfo(TObjectPtr<class UNiagaraDataInterface> DataInterface;)
结果已出,方案已成。

浙公网安备 33010602011771号