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加载与管理 - 知乎

相关推荐
Trouvaille ~17 小时前
【Linux】TCP Socket编程实战(一):API详解与单连接Echo Server
linux·运维·服务器·网络·c++·tcp/ip·socket
坚果派·白晓明17 小时前
在鸿蒙设备上快速验证由lycium工具快速交叉编译的C/C++三方库
c语言·c++·harmonyos·鸿蒙·编程语言·openharmony·三方库
小镇敲码人17 小时前
深入剖析华为CANN框架下的Ops-CV仓库:从入门到实战指南
c++·python·华为·cann
张张努力变强18 小时前
C++ STL string 类:常用接口 + auto + 范围 for全攻略,字符串操作效率拉满
开发语言·数据结构·c++·算法·stl
小镇敲码人18 小时前
探索CANN框架中TBE仓库:张量加速引擎的优化之道
c++·华为·acl·cann·ops-nn
平安的平安18 小时前
面向大模型算子开发的高效编程范式PyPTO深度解析
c++·mfc
June`18 小时前
muduo项目排查错误+测试
linux·c++·github·muduo网络库
C++ 老炮儿的技术栈18 小时前
VS2015 + Qt 实现图形化Hello World(详细步骤)
c语言·开发语言·c++·windows·qt
Once_day18 小时前
C++之《Effective C++》读书总结(4)
c语言·c++·effective c++
柯一梦19 小时前
STL2---深入探索vector的实现
c++