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;)

      结果已出,方案已成。

 

posted @ 2025-06-27 22:08  bodong  阅读(78)  评论(0)    收藏  举报