主要内容
- UObject的序列化和反序列化
- UPackage的保存与加载
- CDO:ClassDefaultObject,UClass的成员变量,表示此UObject的默认对象。
- 序列化:把RUNTIME运行时的内存对象,通过计算算法 存入/ 取出 为二进制内存.
- 存档: 把序列化的一段内存 存/取 到磁盘文件上
- 持久化: 把序列化的一段内存 存/取 到数据库上的操作.
UObject的序列化
- 主要功能:把数据结构或对象状态转换为可存取的格式(比如二进制)
- Serialize()方法:负责对整个类里面的「某些信息」做序列化。
- 其中UPROPERTY()宏标记的属性,一般都是会被序列化的。
- 将序列化的「二进制」数据以.uasset后缀的文件保存起来
- LoadObject可以重新将uasset文件反序列化成UObject
![](https://img.haomeiwen.com/i10337581/aac7378da0ae76e3.png)
FArchive介绍
- UE使用访问者模式,让FArchive做访问者,来访问实现了Serialize(FArchive& Ar)的被访问者。
- 大多数存储使用此类型:TArray<uint8> Buffer / TArray<uint8>& Bytes
- FArchive 通过重载 << 来实现对 UObject 中数据的访问
- 技巧:使用了友元friend,谁实现都可以
- 比如int作为被访问者:FArchive& operator<<(FArchive& Ar, int& Value)
int Num = 1;
FArchive Archive ;
Archive<<Num;
FArchive主要的类
- FArchive系列类有:文件系统、内存统计、UAssert对象存取,网络IO等。
- FLinkerLoad和FLinkerSave :public FLinker, public FArchiveUObject(存取uasset,package,UObject)
- FArchiveFile Reader/Write Generic : public FArchive(文件系统相关)
- FArchiveCountMem : public FArchiveUObject(统计内存使用功能类)
- struct FBitReader 和 FBitWriter: public FBitArchive(字节流写入和读取IO)
UObject::Serialize( FArchive& Ar )
UClass *ObjClass = GetClass();
UObject* LoadOuter = GetOuter();
FName LoadName = GetFName();
if (ObjClass != UClass::StaticClass())
{
SerializeScriptProperties(Ar);
}
// 添加GUID
FLazyObjectPtr::PossiblySerializeObjectGuid(this, Ar);
// 内存统计(with proper alignment to match C++)
SIZE_T Size = GetClass()->GetStructureSize();
Ar.CountBytes( Size, Size );
- 虚幻引擎序列化每个继承自 CLass 的类的默认值(即序列化CDO),然后序列化对象与类默认对象的差异。这样就节约了大量的子类对象序列化后的存储空间。
通过UPackage保存UObject
- 1 创建资源包:UPackage* Package = CreatePackage(*AssetPath);
- 2 对象创建时,关联包 NewObject<UTestObject>(Package, FName(*ObjectName),
- 3 Package->MarkPackageDirty();
- 4 UPackage::SavePackage(Package, pObj......)
FString AssetPath = TEXT("/Game/TestObject");
// 全路径,MyGame/Content/MyObject.uasset
FString PackageFileName = FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension());
FString ObjectName = TEXT("Mike");
// 调用保存
bool bSaved = SaveToPackage(AssetPath, PackageFileName, ObjectName);
UE_LOG(TestLog, Display, TEXT("Save Package %s !"), bSaved?TEXT("Success"):TEXT("Failed"));
// 保存到package函数
bool SaveToPackage(const FString &AssetPath, const FString &PackageFileName, const FString &ObjectName)
{
// 创建一个资源Package
UPackage* Package = CreatePackage(*AssetPath);
// 完全加载这个包
Package->FullyLoad();
// 创建对象
UTestObject* pObj = NewObject<UTestObject>(Package, FName(*ObjectName),
EObjectFlags::RF_Public | EObjectFlags::RF_Standalone);
// 设置对象属性
pObj->CurPlayerName = ObjectName;
pObj->CurPlayerAge = 18;
// 标记脏数据
Package->MarkPackageDirty();
// 保存对象到uasset
return UPackage::SavePackage(Package, pObj,
EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
*PackageFileName, GError, nullptr, true, true, SAVE_NoError);
}
通过UPackage加载UObject
- LoadPackage 先加载包 FullyLoad
- FindObject<UMyObject>(pPkg, TEXT("Mike")); // *ObjectName
- 从包中寻找某名称的对象
// 加载package
UPackage* pPkg = LoadPackage(nullptr, *AssetPath, LOAD_None);
if(pPkg)
{
pPkg->FullyLoad();
auto info = pPkg->GetDetailedInfo();
// 加载UObject上文中保存的名字ObjectName
UMyObject* FoundObj = FindObject<UMyObject>(pPkg, TEXT("Mike"));
if(FoundObj)
{
auto _age = FoundObj->CurPlayerAge;
FString _name = FoundObj->CurPlayerName;
}
}
- 一路跟踪,UE是从这里找来对象指针返回
![](https://img.haomeiwen.com/i10337581/ee7307f1d76ccc19.png)
Object序列化步骤
- 通过GetClass函数获取当前的类信息,通过GetOuter函数获取Outer。Outer指定当前UObject会作为哪一个对象的子对象进行序列化。
- 判断当前等待序列化的对象的类UClass的信息是否被载入,没有的话:
- 预载入当前类的信息;
- 预载入当前类的默认对象CDO的信息(ObjClass->GetDefaultObject())
- 载入名字
- 载入Outer
- 载入当前对象的类信息,保存于ObjClass对象中。
- 载入对象的所有脚本成员变量信息。这一步必须在类信息加载后,才会知道有哪些脚本成员变量需要加载。
- 调用SerializeScriptProperties,序列化在类中定义的对象属性。
- 调用FArchive.MarkScriptSerializationStart,标志脚本序列化数据开始;
- 调用SerializeTaggedProperties,序列化对象属性,并且加入tag;
- 调用FArchive.MarkScriptSerializationEnd,标志脚本序列化数据结束。
-可参考 直呼:UE 序列化介绍及源码解析
网友评论