美文网首页
18 UE5 UObject的序列化和存取

18 UE5 UObject的序列化和存取

作者: 游戏开发程序员 | 来源:发表于2024-03-16 10:53 被阅读0次

    主要内容

    • UObject的序列化和反序列化
    • UPackage的保存与加载
    • CDO:ClassDefaultObject,UClass的成员变量,表示此UObject的默认对象。
    • 序列化:把RUNTIME运行时的内存对象,通过计算算法 存入/ 取出 为二进制内存.
    • 存档: 把序列化的一段内存 存/取 到磁盘文件上
    • 持久化: 把序列化的一段内存 存/取 到数据库上的操作.

    UObject的序列化

    • 主要功能:把数据结构或对象状态转换为可存取的格式(比如二进制)
    • Serialize()方法:负责对整个类里面的「某些信息」做序列化。
    • 其中UPROPERTY()宏标记的属性,一般都是会被序列化的。
    • 将序列化的「二进制」数据以.uasset后缀的文件保存起来
    • LoadObject可以重新将uasset文件反序列化成UObject
    image.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是从这里找来对象指针返回
    image.png

    Object序列化步骤

    1. 通过GetClass函数获取当前的类信息,通过GetOuter函数获取Outer。Outer指定当前UObject会作为哪一个对象的子对象进行序列化。
    2. 判断当前等待序列化的对象的类UClass的信息是否被载入,没有的话:
    3. 预载入当前类的信息;
    4. 预载入当前类的默认对象CDO的信息(ObjClass->GetDefaultObject())
    5. 载入名字
    6. 载入Outer
    7. 载入当前对象的类信息,保存于ObjClass对象中。
    8. 载入对象的所有脚本成员变量信息。这一步必须在类信息加载后,才会知道有哪些脚本成员变量需要加载。
    9. 调用SerializeScriptProperties,序列化在类中定义的对象属性。
    10. 调用FArchive.MarkScriptSerializationStart,标志脚本序列化数据开始;
    11. 调用SerializeTaggedProperties,序列化对象属性,并且加入tag;
    12. 调用FArchive.MarkScriptSerializationEnd,标志脚本序列化数据结束。

    -可参考 直呼:UE 序列化介绍及源码解析

    相关文章

      网友评论

          本文标题:18 UE5 UObject的序列化和存取

          本文链接:https://www.haomeiwen.com/subject/vghwzdtx.html