UE 将UObject写到Package,加载Package获取UObject

1,将UObject写到Package中

cpp 复制代码
//CachedMesh 就是要写的UObject对象	
UStaticMesh* CachedMesh = CachedFontData.GetGlyphMesh(ShapedGlyph.GlyphIndex, TextParamValue.bOutline, TextParamValue.Extrude, TextParamValue.Bevel, TextParamValue.BevelType, TextParamValue.BevelSegments, TextParamValue.ArcRadius);
				
FString objectName = TEXT("MyCreateObject");

// 创建包
//FString packageName = TEXT("/Game/") + saveFolder/* + TEXT("/Test3D_") + FString::FromInt(index)*/;
FString packageName = TEXT("/、Meshs/Text3D")/* + saveFolder + TEXT("/Test3D_") + FString::FromInt(index)*/;
UPackage* package = CreatePackage(*packageName);
package->MarkPackageDirty();
FString StaticMeshName = (TEXT("Test3D_") + CachedMesh->GetFName().ToString() + TEXT("_") + FString::FromInt(ShapedGlyph.GlyphIndex));
UStaticMesh* AssetOnly = DuplicateObject(CachedMesh, package, FName(*StaticMeshName));
AssetOnly->SetFlags(RF_Public | RF_NeedPostLoadSubobjects | RF_Standalone);
FString packageNameEX;
packageName = packageName + TEXT("/Test3D_") + CachedMesh->GetFName().ToString() + TEXT("_") + FString::FromInt(ShapedGlyph.GlyphIndex);
FPackageName::TryConvertLongPackageNameToFilename(packageName, packageNameEX, TEXT(".uasset"));

// 保存Package
FSavePackageArgs SaveArgs = { nullptr, nullptr, EObjectFlags::RF_Standalone | EObjectFlags::RF_Public, SAVE_None, false, true, true, FDateTime::MinValue(), GError, nullptr };
UPackage::Save(package, AssetOnly, *packageNameEX, SaveArgs);

2,加载Package,读取其中的UObject

cpp 复制代码
	TextParam TextParamValue{};
	FString packageName = TEXT("/Meshs/Text3D");
	IFileManager& MyFileManager = IFileManager::Get();
	FString packageNameEX;
	FPackageName::TryConvertLongPackageNameToFilename(packageName, packageNameEX, TEXT(""));
	if (MyFileManager.DirectoryExists(*packageNameEX))
	{
		TArray<FString> AssetName;
		MyFileManager.FindFiles(AssetName, *packageNameEX, TEXT("*.uasset"));
		FPXCachedFontData& CachedFontData = GetCachedFontData(DefaultFont);
		for (int i = 0; i < AssetName.Num(); i++)
		{
			TSet<FName> LoadTags = {};
			FString PackageName = packageNameEX + TEXT("/") + AssetName[i];
			UPackage* FoundPackage = FindObjectFast<UPackage>(nullptr, FName(*PackageName));
			if (FoundPackage == nullptr)
			{
				FLinkerInstancingContext InstancingContext(MoveTemp(LoadTags));
				FoundPackage = LoadPackage(nullptr, *PackageName, LOAD_None, nullptr, &InstancingContext);
				if (FoundPackage == nullptr)
					continue;
			}
			FString StaticMeshName = AssetName[i];
			StaticMeshName.RemoveFromEnd(TEXT(".uasset"));
			FoundPackage->GetLinker()->LoadAllObjects(true);
			UObject* CurrentObject = FoundPackage;
			CurrentObject = StaticFindObjectFastInternal(nullptr, CurrentObject, *StaticMeshName);
			UStaticMesh* StaticMesh = dynamic_cast<UStaticMesh*>(CurrentObject);
			if (StaticMesh == nullptr)
				continue;
			StaticMeshName = StaticMesh->GetName();
			TArray<FString> stringArray;
			StaticMeshName.ParseIntoArray(stringArray, TEXT("_"), false);
            //这步不需要,是项目的代码
			CachedFontData.AddGlyphMesh(StaticMesh, FCString::Atoi(*(stringArray.Last())), TextParamValue.bOutline, TextParamValue.Extrude, TextParamValue.Bevel, TextParamValue.BevelType, TextParamValue.BevelSegments, TextParamValue.ArcRadius);
		}
	}

3,代码解析

LoadObject

LoadObject主要是把磁盘中的东西加载到一个UObject里面去,因为涉及的对象可能会引用其他的资产,所以会触发一个递归的加载。过程如下:

  1. CreatePackage: 因为每个UObject的最外层outer都是UPackage,所以会先调用CreatePackage创建一个UPackage,比如我们加载的路径是"Game/http://xx.xxx",那么"Game/xx"就是包名,创建的具体方法就是NewObject<UPacakge>。

  2. **FLinkerLoad::CreateLinker:**创建一个LinkerLoad,LinkerLoad主要负责去磁盘中加载UObject的数据,然后管理对应资产之间的一些引用。FLinkerLoad继承了FLinkTables,里面有一个导入和导出模块的定义。相关代码在下方

  3. **FLinkerLoad::LoadAllObjects:**加载包里所有的UObject,这里就是根据上面的ExportMap中的UClass信息,生成对应的UObject,然后把数据反序列化进去。

  4. **StaticFindObjectFast:**根据ObjectName找到加载完成的UObject并返回

    class FLinkerTables
    {
    /** The list of FObjectImports found in the package /
    //这个是这个包引用的对象,如果资产引用了其他资产,在这里会有体现
    TArray<FObjectImport> ImportMap;
    /
    * The list of FObjectExports found in the package */
    //这个是这个包要导出的对象,我们LoadObject需要加载的对象都在这里面记录
    TArray<FObjectExport> ExportMap;
    }

LoadClass

LoadClass本质就是调用了LoadObject,代码如下:

UClass* StaticLoadClass( UClass* BaseClass, UObject* InOuter, const TCHAR* InName, const TCHAR* Filename, uint32 LoadFlags, UPackageMap* Sandbox )
{
    UClass* Class = LoadObject<UClass>( InOuter, InName, Filename, LoadFlags, Sandbox );
    return Class;
}

关于LoadClass的路径,LoadClass填路径要按照这样的格式"Game/xxx.xx_C",相比LoadObject,路径名后面多了一个"_C"。这种路径分为两个部分,一个是包名,也就是"."(点)前面的部分为。一个是ObjectName,UClass的ObjectName多个"_C"。在LoadObject的时候,会先把所有相关的UObject加载好,在FUObjectHashTables进行管理,然后根据这个ObjectName进行查找,所以如果LoadClass的时候后面不加个"_C",UClass是被加载到内存里的,但是就是在后面查找的时候会查找不到。

FindObject

有了上面对UObject的管理,UE也提供了FindObject这个接口用于查找Object,相关代码在StaticFindObjectFastInternalThreadSafe里面。内容也很简单,就不贴具体代码了,就是在上面提到的FUObjectHashTables里面找,先从HashOuter,找不到就到PackageToObjectListMap,再找不到就到Hash里面找。

参考:UE5:UObject加载与管理 - 知乎

相关推荐
draracle7 分钟前
如何将 cryptopp库移植到UE5内
ue5
冷白白14 分钟前
【C++】C++对象初探及友元
c语言·开发语言·c++·算法
睡觉然后上课33 分钟前
c基础面试题
c语言·开发语言·c++·面试
qing_04060340 分钟前
C++——继承
开发语言·c++·继承
ya888g41 分钟前
GESP C++四级样题卷
java·c++·算法
小叶学C++1 小时前
【C++】类与对象(下)
java·开发语言·c++
NuyoahC1 小时前
算法笔记(十一)——优先级队列(堆)
c++·笔记·算法·优先级队列
FL16238631292 小时前
[C++]使用纯opencv部署yolov11-pose姿态估计onnx模型
c++·opencv·yolo
sukalot2 小时前
windows C++-使用任务和 XML HTTP 请求进行连接(一)
c++·windows
ぃ扶摇ぅ2 小时前
Windows系统编程(三)进程与线程二
c++·windows