From 5e30b2af4e6aa27cc35003dbdfb6390c1b0dc738 Mon Sep 17 00:00:00 2001 From: donghaiwang Date: Tue, 21 Jul 2026 14:12:37 +0800 Subject: [PATCH] =?UTF-8?q?[4.27-plus]=20=E5=B0=86=20UBlueprintGeneratedCl?= =?UTF-8?q?asses=20=E6=94=B9=E4=B8=BA=E8=B5=84=E6=BA=90=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E4=BE=BF=E5=9C=A8=E7=BC=96=E8=BE=91=E5=99=A8=E4=B8=AD=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E5=B7=B2=E7=94=9F=E6=88=90=E7=9A=84=E8=93=9D=E5=9B=BE?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 但当 BPGC 位于未生成的包中时,请将其从全局 AssetRegistry 中移除,以避免与现有的 UBlueprint 资源冲突。 #jira UE-78220 Change UBlueprintGeneratedClasses to be assets so that cooked blueprints are viewable in editor. But omit BPGC from the global AssetRegistry when they are in uncooked packages to avoid conflicting with the existing UBlueprint asset. #cherrypick of 15584115 by Matt.Peters #rb Francis.Hurteau #rnx #ushell-cherrypick of 15584238 by Matt.Peters #ROBOMERGE-SOURCE: CL 15587769 via CL 15587806 via CL 15587835 #ROBOMERGE-BOT: RELEASE (Release-Engine-Staging -> Main) (v777-15581079) [CL 15587846 by matt peters in Main branch] https://github.com/EpicGames/UnrealEngine/commit/38588dccb3c2022cc7f806529bd6dad134874cad 没有修改:Engine/Source/Editor/UnrealEd/Private/Commandlets/AssetRegistryGenerator.cpp‎ 中的 new FAssetData(&Object, true /* bAllowBlueprintClass */); --- .../AssetRegistry/Private/AssetRegistry.cpp | 49 ++++++++++++++++--- .../AssetRegistry/Private/AssetRegistry.h | 3 ++ .../Private/AssetRegistryState.cpp | 9 +++- .../Classes/Engine/BlueprintGeneratedClass.h | 2 + .../Private/BlueprintGeneratedClass.cpp | 13 ++++- 5 files changed, 66 insertions(+), 10 deletions(-) diff --git a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp index 7593988c9..36f3cd5a9 100644 --- a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp +++ b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp @@ -773,6 +773,14 @@ bool UAssetRegistryImpl::EnumerateAssets(const FARCompiledFilter& InFilter, TFun return; } +#if WITH_EDITOR + // Skip classes that report themselves as assets but that the editor AssetRegistry is currently not counting as assets + if (ShouldSkipAsset(Obj->GetClass()->GetFName(), InMemoryPackage->GetPackageFlags())) + { + return; + } +#endif + // Package name const FName PackageName = InMemoryPackage->GetFName(); @@ -2117,9 +2125,17 @@ void UAssetRegistryImpl::AssetSearchDataGathered(const double TickStartTime, TBa // Add the found assets while (AssetResults.Num() > 0) { - FAssetData*& BackgroundResult = AssetResults.Pop(); + // Delete or take ownership of the BackgroundResult; it was originally new'd by an FPackageReader + TUniquePtr BackgroundResult(AssetResults.Pop()); + CA_ASSUME(BackgroundResult.Get() != nullptr); - CA_ASSUME(BackgroundResult); +#if WITH_EDITOR + // Skip Assets that we filter out + if (ShouldSkipAsset(BackgroundResult->AssetClass, BackgroundResult->PackageFlags)) + { + continue; + } +#endif // Try to update any asset data that may already exist FAssetData* AssetData = State.CachedAssetsByObjectPath.FindRef(BackgroundResult->ObjectPath); @@ -2128,20 +2144,16 @@ void UAssetRegistryImpl::AssetSearchDataGathered(const double TickStartTime, TBa if (AssetData) { // If this ensure fires then we've somehow processed the same result more than once, and that should never happen - if (ensure(AssetData != BackgroundResult)) + if (ensure(AssetData != BackgroundResult.Get())) { // The asset exists in the cache, update it UpdateAssetData(AssetData, *BackgroundResult); - - // Delete the result that was originally created by an FPackageReader - delete BackgroundResult; - BackgroundResult = nullptr; } } else { // The asset isn't in the cache yet, add it and notify subscribers - AddAssetData(BackgroundResult); + AddAssetData(BackgroundResult.Release()); } // Populate the path tree @@ -2661,8 +2673,29 @@ void UAssetRegistryImpl::OnDirectoryChanged(const TArray& FileC ScanModifiedAssetFiles(ModifiedFiles); } +bool UAssetRegistryImpl::ShouldSkipAsset(FName AssetClass, uint32 PackageFlags) const +{ + // We do not yet support having UBlueprintGeneratedClasses be assets when the UBlueprint is also + // an asset; the content browser does not handle the multiple assets correctly and displays this + // class asset as if it is in a separate package. Revisit when we have removed the UBlueprint as an asset + // or when we support multiple assets. + static FName NAME_BlueprintGeneratedClass = TEXT("BlueprintGeneratedClass"); + bool bIsCooked = (PackageFlags & PKG_FilterEditorOnly) != 0; + if (!bIsCooked && AssetClass == NAME_BlueprintGeneratedClass) + { + return true; + } + return false; +} + void UAssetRegistryImpl::OnAssetLoaded(UObject *AssetLoaded) { + UPackage* Package = AssetLoaded->GetPackage(); + if (Package && ShouldSkipAsset(AssetLoaded->GetClass()->GetFName(), Package->GetPackageFlags())) + { + return; + } + LoadedAssetsToProcess.Add(AssetLoaded); } diff --git a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h index cec717ed7..d3fff4882 100644 --- a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h +++ b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h @@ -213,6 +213,9 @@ class UAssetRegistryImpl : public UObject, public IAssetRegistry /** Called when a file in a content directory changes on disk */ void OnDirectoryChanged(const TArray& Files); + /** Called to check whether we should filter out assets of the given class and packageflags from the editor's AssetRegistry */ + bool ShouldSkipAsset(FName AssetClass, uint32 PackageFlags) const; + /** Called when an asset is loaded, it will possibly update the cache */ void OnAssetLoaded(UObject *AssetLoaded); diff --git a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistryState.cpp b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistryState.cpp index ecad60839..dda1f8c44 100644 --- a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistryState.cpp +++ b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistryState.cpp @@ -1492,11 +1492,18 @@ void FAssetRegistryState::AddAssetData(FAssetData* AssetData) { NumAssets++; + FAssetData*& ExistingByObjectPath = CachedAssetsByObjectPath.FindOrAdd(AssetData->ObjectPath); + if (ExistingByObjectPath) + { + UE_LOG(LogAssetRegistry, Error, TEXT("AddAssetData called with ObjectPath %s which already exists. ") + TEXT("This will overwrite and leak the existing AssetData."), *AssetData->ObjectPath.ToString()); + } + ExistingByObjectPath = AssetData; + TArray& PackageAssets = CachedAssetsByPackageName.FindOrAdd(AssetData->PackageName); TArray& PathAssets = CachedAssetsByPath.FindOrAdd(AssetData->PackagePath); TArray& ClassAssets = CachedAssetsByClass.FindOrAdd(AssetData->AssetClass); - CachedAssetsByObjectPath.Add(AssetData->ObjectPath, AssetData); PackageAssets.Add(AssetData); PathAssets.Add(AssetData); ClassAssets.Add(AssetData); diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h b/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h index 986005847..d443f8bf2 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h @@ -746,6 +746,8 @@ class ENGINE_API UBlueprintGeneratedClass : public UClass #if WITH_EDITOR virtual UClass* RegenerateClass(UClass* ClassToRegenerate, UObject* PreviousCDO) override; #endif //WITH_EDITOR + virtual bool IsAsset() const override; + // End UObject interface // UClass interface diff --git a/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp b/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp index 1f8b94bc6..614cf4308 100644 --- a/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp +++ b/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp @@ -162,8 +162,11 @@ void UBlueprintGeneratedClass::PostLoad() FPrimaryAssetId UBlueprintGeneratedClass::GetPrimaryAssetId() const { FPrimaryAssetId AssetId; - if (!ensure(ClassDefaultObject)) + if (!ClassDefaultObject) { + // All UBlueprintGeneratedClass objects should have a pointer to their generated ClassDefaultObject, except for the + // CDO itself of the UBlueprintGeneratedClass class. + verify(HasAnyFlags(RF_ClassDefaultObject)); return AssetId; } @@ -1583,6 +1586,14 @@ UClass* UBlueprintGeneratedClass::RegenerateClass(UClass* ClassToRegenerate, UOb } #endif +bool UBlueprintGeneratedClass::IsAsset() const +{ + // UClass::IsAsset returns false; override that to return true for BlueprintGeneratedClasses, + // but only if the instance satisfies the regular definition of an asset (RF_Public, not transient, etc) + // and only if it is the active BPGC matching the Blueprint + return UObject::IsAsset() && !HasAnyClassFlags(CLASS_NewerVersionExists); +} + void UBlueprintGeneratedClass::Link(FArchive& Ar, bool bRelinkExistingProperties) { Super::Link(Ar, bRelinkExistingProperties);