利用SaveGame标记保存属性

本文介绍如何在Unreal Engine中利用SaveGame标记来实现自定义对象的序列化和保存。通过继承Archive类并重写<<函数,区分需要序列化的UObject类型和Actor,以及处理IsAsset()为true的特殊对象。同时,将这些功能暴露给蓝图,以便在蓝图层面进行数据保存和读取操作。
摘要由CSDN通过智能技术生成

Unreal中的SaveGame标记

SaveGame标记
虚幻中提供了SaveGame类型作为保存游戏的对象,用户可以将需保存的数据直接赋值至SaveGame子类的数据中再将该对象SaveToSlot就可以保存至本地。

https://docs.unrealengine.com/latest/CHN/Gameplay/SaveGame/index.html

但是细心的用户可以在属性的高级设置中看到SaveGame标记,那是否可以像Java之类高级语言一样直接序列化这个对象呢?
我们知道虚幻已经魔改了C++,使之具有了反射的能力,那么序列化的功能是必然具有的,所以下面我们就需要将对象序列化的功能自行改进,并且暴露给蓝图。

Archive类型

Base class for archives that can be used for loading, saving, and garbage collecting in a byte order neutral way.

从Archive类的描述中可以得知这个类型可用于保存和读取,我们就从改装这个类开始。

区分需要序列化的类型

有些类型,比如说UTexture2D、USoundBase之类的对象是已经确定存在与游戏包内,所以不必完全序列化该对象,只需要通过保存的路径读取即可。通过IsAsset()函数可区别。

区分是UObject还是Actor

Actor是需要生成在场景中的,而且组件没法添加SaveGame标记,所以要单独区分。

enum class ESaveLoadType : uint8
{
   
    Static, //不需要序列化,直接读取路径即可
    Normal, //需要序列化的UObject类型
    Actor //Actor类型
};

FORCEINLINE FArchive& operator<<(FArchive& Ar, const ESaveLoadType& SaveLoadType)
{
    uint8 SaveLoadEnumType = (uint8)SaveLoadType;
    Ar << SaveLoadEnumType;
    return Ar;
}

FORCEINLINE FArchive& operator<<(FArchive& Ar, ESaveLoadType& SaveLoadType)
{
    uint8 SaveLoadEnumType = (uint8)SaveLoadType;
    Ar << SaveLoadEnumType;

    if (Ar.IsLoading())
    {
        SaveLoadType = (ESaveLoadType)SaveLoadEnumType;
    }
    return Ar;
}

//假如需要序列化Component继承该接口,当然也可使用别的放法,比如Tag来识别
UINTERFACE(BlueprintType)
class UNeedSaveComponent :public UInterface
{
   
    GENERATED_UINTERFACE_BODY()
};

class INeedSaveComponent
{
   
    GENERATED_IINTERFACE_BODY()
};

继承Archive的子类并且重写<<函数

//序列化使用的类
struct FGameWriteArchive : public FBufferArchive
{

    FGameWriteArchive(bool bIsPersistent = false, const FName InArchiveName = NAME_None)
        :FBufferArchive(bIsPersistent, InArchiveName)
    {
        ArIsSaveGame = true; //为真即序列化被标记为SaveGame的属性
    }

    virtual FArchive& operator<<(class UObject*& Obj) override;
}; 

//反序列化使用的类
class FGameReadArchive final : public FMemoryArchive
{
public:
    UWorld* CurrentWorld;//SpawnActor需要提供World信息

    virtual FString GetArchiveName() const { return TEXT("FSaveGameReadArchive"); }

    int64 TotalSize()
    {
        return FMath::Min((int64) Bytes.Num(), LimitSize);
    }

    void Serialize(void* Data, int64 Num)
    {
        if (Num && !ArIsError)
        {
            // Only serialize if we have the requested amount of data
            if (Offset + Num <= TotalSize())
            {
                FMemory::Memcpy(Data, &Bytes[Offset], Num);
                Offset += Num;
            }
            else
            {
                ArIsError = true;
            }
        }
    }

    FGameReadArchive(const TArray<uint8>& InBytes, UWorld* CurrentWorld, bool bIsPersistent = false)
        : FMemoryArchive()
        , Bytes(InBytes)
        , CurrentWorld(CurrentWorld)
        , LimitSize(INT64_MAX)
    {
        ArIsLoading = true;//表示这个是反序列化的类
        ArIsPersistent = bIsPersistent;
    }

    /** With this method it's possible to attach data behind some serialized data. */
    void SetLimitSize(int64 NewLimitSize)
    {
        LimitSize = NewLimitSize;
    }

    virtual FArchive& operator<<(class UObject*& Obj) override;

protected:

    const TArray<uint8>& Bytes;
    int64 LimitSize;
};

实现

//不需要序列化的类型填这里
TArray<UClass*> StaticObjectClass
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值