Допустим, вы создали собственную USTRUCT на C++ и теперь хотите её сериализовать.
USTRUCT(BlueprintType)
структура FComplexStruct
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FNonSerializableStruct FirstStruct;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FSerializableStruct SecondStruct;
};
Обычно достаточно просто отметить нужные поля с помощью функции SaveGame.
USTRUCT(BlueprintType)
структура 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);
вернуть Ar;
}
шаблон <>
struct TStructOpsTypeTraits<FComplexStruct> : public TStructOpsTypeTraitsBase2<FComplexStruct>
{
перечисление
{
WithSerializer = true
};
};
Здесь, в шаблонной структуре, мы сообщаем движку, что хотим вручную обрабатывать сериализацию для нашей структуры.
Перегрузка оператора << добавлена просто для удобства — чтобы нам не приходилось вызывать метод Serialize вручную каждый раз, когда мы используем эту структуру в качестве члена в другой структуре или классе.
Итак, что дальше?
Мы еще не написали логику сериализации.
Как я уже упоминал ранее, нам нужно будет добавить метод Serialize к нашей структуре.
Unreal будет вызывать его автоматически при необходимости — благодаря коду, который мы добавили выше.
bool Serialize(FArchive& Ar)
{
// Сериализация партиями, поскольку поддержка отсутствует.
Ar << FirstStruct.Type;
Ar << FirstStruct.Class;
Ar << FirstStruct.Object;
// Сериализуйте весь код целиком, поскольку такая возможность поддерживается.
Ar << SecondStruct;
вернуть true;
}
#define MAKE_SERIALIZABLE_STRUCT(Name) \
inline FArchive& operator<<(FArchive& Ar, Name& Save) \
{ \
Save.Serialize(Ar);
return Ar;
} \
шаблон<> \
struct TStructOpsTypeTraits<Name> : public TStructOpsTypeTraitsBase2<Name> \
{ \
перечисление \
{ \
WithSerializer = true, \
}; \
};
Теперь мы можем просто включить наш макрос в заголовочный файл и добавить поддержку сериализации для любой из наших структур чистым и лаконичным способом.
USTRUCT(BlueprintType)
структура FComplexStruct
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FNonSerializableStruct FirstStruct;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FSerializableStruct SecondStruct;
};
Обычно достаточно просто отметить нужные поля с помощью функции SaveGame.
USTRUCT(BlueprintType)
структура 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);
вернуть Ar;
}
шаблон <>
struct TStructOpsTypeTraits<FComplexStruct> : public TStructOpsTypeTraitsBase2<FComplexStruct>
{
перечисление
{
WithSerializer = true
};
};
Здесь, в шаблонной структуре, мы сообщаем движку, что хотим вручную обрабатывать сериализацию для нашей структуры.
Перегрузка оператора << добавлена просто для удобства — чтобы нам не приходилось вызывать метод Serialize вручную каждый раз, когда мы используем эту структуру в качестве члена в другой структуре или классе.
Итак, что дальше?
Мы еще не написали логику сериализации.
Как я уже упоминал ранее, нам нужно будет добавить метод Serialize к нашей структуре.
Unreal будет вызывать его автоматически при необходимости — благодаря коду, который мы добавили выше.
bool Serialize(FArchive& Ar)
{
// Сериализация партиями, поскольку поддержка отсутствует.
Ar << FirstStruct.Type;
Ar << FirstStruct.Class;
Ar << FirstStruct.Object;
// Сериализуйте весь код целиком, поскольку такая возможность поддерживается.
Ar << SecondStruct;
вернуть true;
}
Улучшение
Вы, вероятно, заметите, что код для добавления поддержки сериализации довольно шаблонный. И вы знаете, что это значит — мы можем обернуть его в макрос, чтобы значительно упростить добавление поддержки других структур!#define MAKE_SERIALIZABLE_STRUCT(Name) \
inline FArchive& operator<<(FArchive& Ar, Name& Save) \
{ \
Save.Serialize(Ar);
return Ar;
} \
шаблон<> \
struct TStructOpsTypeTraits<Name> : public TStructOpsTypeTraitsBase2<Name> \
{ \
перечисление \
{ \
WithSerializer = true, \
}; \
};
Теперь мы можем просто включить наш макрос в заголовочный файл и добавить поддержку сериализации для любой из наших структур чистым и лаконичным способом.