Введение
Допустим, вы создали свою 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, даже если их поля изначально не поддерживают эту функцию.