Введение

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

Комментарии (0)