Кастомная сериализация структур в UE

от автора

Введение

Допустим, вы создали свою USTRUCT в C++ и хотите её сериализовать.

USTRUCT(BlueprintType) struct FComplexStruct { GENERATED_BODY()  UPROPERTY(EditAnywhere, BlueprintReadWrite) FNonSerializableStruct FirstStruct;  UPROPERTY(EditAnywhere, BlueprintReadWrite) FSerializableStruct SecondStruct; };

Обычно, достаточно просто пометить нужные поля как SaveGame.

USTRUCT(BlueprintType) struct FComplexStruct {     GENERATED_BODY()      // Не поддерживает сериализацию     UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame)     FNonSerializableStruct FirstStruct;      // Поддерживает сериализацию     UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame)     FSerializableStruct SecondStruct; }; 

Но вот проблема, для этого они сами должны поддерживать сериализацию. К сожалению, одна из наших переменных ее не поддерживает. В моем случае, это структура FNonSerializableStruct. Из-за этого сериализуется только вторая структура, хоть мы и пометили SaveGame обе.

Решение

И как же тогда быть? В таком случае, нам придется вручную сериализовать нашу структуру. Благо, это довольно просто сделать. Для этого нам нужно за пределами структуры, глобально, для файла, добавить следующее:

inline FArchive& operator<<(FArchive& Ar, FComplexStruct& Save) { Save.Serialize(Ar); return Ar; }  template <> struct TStructOpsTypeTraits<FComplexStruct> : public TStructOpsTypeTraitsBase2<FComplexStruct> { enum { WithSerializer = true }; };

Тут, в шаблонной структуре, мы говорим движку, что мы хотим вручную сериализовать нашу структуру.

Перегрузка оператора << добавлена чисто для удобства, дабы не вызывать всегда метод Serialize руками, если мы захотим использовать нашу структуру как поле другой структуры/класса.

Окей, что делать дальше? Мы ведь так и не написали нашу сериализацию.

Я уже упомянул выше метод Serialize. Его нам и надо добавить в нашу структуру. Движок его вызовет сам, когда надо. Сделает он это благодаря коду, который мы написали выше.

bool Serialize(FArchive& Ar) { // Сериализуем частями, т.к. поддержки нет     Ar << FirstStruct.Type;     Ar << FirstStruct.Class;     Ar << FirstStruct.Object;      // Сериализуем целиком, т.к. есть поддержка     Ar << SecondStruct;  return true; }

Полный код

USTRUCT(BlueprintType) struct FComplexStruct {     GENERATED_BODY()      // Не поддерживает сериализацию     UPROPERTY(EditAnywhere, BlueprintReadWrite)     FNonSerializableStruct FirstStruct;      // Поддерживает сериализацию     UPROPERTY(EditAnywhere, BlueprintReadWrite)     FSerializableStruct SecondStruct;      bool Serialize(FArchive& Ar)     {         // Сериализуем частями, т.к. поддержки нет         Ar << FirstStruct.Type;     Ar << FirstStruct.Class;     Ar << FirstStruct.Object;          // Сериализуем целиком, т.к. есть поддержка     Ar << SecondStruct;        return true;     } };  inline FArchive& operator<<(FArchive& Ar, FComplexStruct& Save) { Save.Serialize(Ar); return Ar; }  template <> struct TStructOpsTypeTraits<FComplexStruct> : public TStructOpsTypeTraitsBase2<FComplexStruct> { enum { WithSerializer = true }; }; 

Улучшение

Можно заметить, что код добавления поддержки сериализации довольно шаблонный. Что это означает? Мы можем сделать макрос, дабы сильно облегчить добавление поддержки для других структур!

#define MAKE_SERIALIZABLE_STRUCT(Name) \ inline FArchive& operator<<(FArchive& Ar, Name& Save) \ { \ Save.Serialize(Ar); \ return Ar; \ } \ template<> \ struct TStructOpsTypeTraits<Name> : public TStructOpsTypeTraitsBase2<Name> \ { \ enum  \ { \ WithSerializer = true, \ }; \ };

Теперь мы можем просто закинуть наш макрос в какой-нибудь хедер и довольно лаконично добавить поддержку сериализации для любой нашей структуры.

Финальный код

USTRUCT(BlueprintType) struct FComplexStruct {     GENERATED_BODY()      // Не поддерживает сериализацию     UPROPERTY(EditAnywhere, BlueprintReadWrite)     FNonSerializableStruct FirstStruct;      // Поддерживает сериализацию     UPROPERTY(EditAnywhere, BlueprintReadWrite)     FSerializableStruct SecondStruct;      bool Serialize(FArchive& Ar)     {         // Сериализуем частями, т.к. поддержки нет         Ar << FirstStruct.Type;     Ar << FirstStruct.Class;     Ar << FirstStruct.Object;          // Сериализуем целиком, т.к. есть поддержка     Ar << SecondStruct;        return true;     } };  MAKE_SERIALIZABLE_STRUCT(FComplexStruct);

Итог

Теперь вы можете без труда добавлять поддержку сериализации для собственных структур в Unreal Engine, даже если их поля изначально не поддерживают эту функцию.


ссылка на оригинал статьи https://habr.com/ru/articles/865170/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *