1,将UObject写到Package中
//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
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里面去,因为涉及的对象可能会引用其他的资产,所以会触发一个递归的加载。过程如下:
- CreatePackage:因为每个UObject的最外层outer都是UPackage,所以会先调用CreatePackage创建一个UPackage,比如我们加载的路径是"Game/http://xx.xxx",那么"Game/xx"就是包名,创建的具体方法就是NewObject<UPacakge>。
- FLinkerLoad::CreateLinker:创建一个LinkerLoad,LinkerLoad主要负责去磁盘中加载UObject的数据,然后管理对应资产之间的一些引用。FLinkerLoad继承了FLinkTables,里面有一个导入和导出模块的定义。相关代码在下方
- FLinkerLoad::LoadAllObjects:加载包里所有的UObject,这里就是根据上面的ExportMap中的UClass信息,生成对应的UObject,然后把数据反序列化进去。
- 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里面找。