I’m using the Archive class to implement a save game system. My goal is to serialize any actor and its variables which enable the SaveGame property flag.
So I made an Archive inherits FObjectAndNameAsStringProxyArchive and set ArIsSaveGame to true, and I can enable the SaveGame of any varialbe and they are serialized.
But I found that this works inappropriate for the variables of Blueprint structure type. Even if I check the SaveGame for it, the members are not serialized.
bool UProperty::ShouldSerializeValue( FArchive& Ar ) const
{
if (Ar.ShouldSkipProperty(this))
return false;
if (Ar.IsSaveGame() && !(PropertyFlags & CPF_SaveGame))
return false;
static uint64 SkipFlags = CPF_Transient | CPF_DuplicateTransient | CPF_NonPIEDuplicateTransient | CPF_NonTransactional | CPF_Deprecated | CPF_DevelopmentAssets;
if (!(PropertyFlags & SkipFlags))
return true;
If my archive is Ar.IsSaveGame() == true, then the propery must be CPF_SaveGame enabled, however the Blueprint structure properties are not enabled, so they can’t be saved. So I think UE should provide the way to enable CPF_SaveGame for BP structure properties.
Hi:
Thanks for the link, I’ve already know that, and my question is about to serialize a Blueprint struct, not CPP struct. The question is how to serialize a Blueprint struct when Archive is set to ArIsSaveGame == true.
If you are still having issues, update your post with what your problem is.
– Follow up
This works the same way in C++
CSaveGame.h
#pragma once
#include "GameFramework/SaveGame.h"
#include "CSaveGame.generated.h"
// Structure that is exposed to BP
USTRUCT(BlueprintType)
struct FTestStruct
{
GENERATED_USTRUCT_BODY();
UPROPERTY(BlueprintReadWrite)
bool BoolVar;
UPROPERTY(BlueprintReadWrite)
int IntVar;
UPROPERTY(BlueprintReadWrite)
float FloatVar;
UPROPERTY(BlueprintReadWrite)
uint8 ByteVar;
UPROPERTY(BlueprintReadWrite)
FVector VectorVar;
UPROPERTY(BlueprintReadWrite)
FRotator RotatorVar;
UPROPERTY(BlueprintReadWrite)
FTransform TransformVar;
UPROPERTY(BlueprintReadWrite)
FString StringVar;
};
UCLASS()
class AH452892_API UCSaveGame : public USaveGame
{
GENERATED_BODY()
public:
UCSaveGame( );
// ID for save game
int UserId;
// Slot name for save game
FString SaveSlotName;
// Data to save
FTestStruct SavedTestStruct;
};
CSaveGame.cpp
#include "AH452892.h"
#include "CSaveGame.h"
UCSaveGame::UCSaveGame( )
{
// These can change and probably should with more complex games
UserId = 1;
SaveSlotName = FString( "UCSaveGame" );
}
SerializeActor.h
#pragma once
#include "GameFramework/Actor.h"
// Need CSaveGame for FtestStruct
#include "CSaveGame.h"
#include "SerializeActor.generated.h"
UCLASS()
class AH452892_API ASerializeActor : public AActor
{
GENERATED_BODY()
public:
ASerializeActor();
virtual void BeginPlay() override;
// Local TestStruct data
UPROPERTY(BlueprintReadWrite, SaveGame)
FTestStruct TestStruct;
// Save data to SaveGameObject
UFUNCTION( BlueprintCallable, Category = "Save|Save Test Struct" )
bool SaveTestStruct( FTestStruct In );
// Load data from SaveGameObject
UFUNCTION( BlueprintCallable, Category = "Save|Load Saved Test Struct" )
FTestStruct LoadTestStruct( );
private:
// Save game object
USaveGame *SaveGameObject;
};
SerializeActor.cpp
#include "AH452892.h"
#include "SerializeActor.h"
ASerializeActor::ASerializeActor()
{
// No need for this to tick
PrimaryActorTick.bCanEverTick = false;
}
// On load
void ASerializeActor::BeginPlay()
{
Super::BeginPlay();
// Create SaveGameObject
SaveGameObject = UGameplayStatics::CreateSaveGameObject( UCSaveGame::StaticClass( ) );
}
// Save test struct to our SaveGame
bool ASerializeActor::SaveTestStruct( FTestStruct In )
{
if( SaveGameObject )
{
UCSaveGame *RealSaveGame = Cast<UCSaveGame>( SaveGameObject );
if( RealSaveGame )
{
RealSaveGame->SavedTestStruct = In;
return UGameplayStatics::SaveGameToSlot( RealSaveGame, RealSaveGame->SaveSlotName, RealSaveGame->UserId );
}
}
return false;
}
// Load test struct from our SaveGame
FTestStruct ASerializeActor::LoadTestStruct( )
{
if( SaveGameObject )
{
UCSaveGame *RealSaveGame = Cast<UCSaveGame>(SaveGameObject);
if( RealSaveGame )
{
TestStruct = RealSaveGame->SavedTestStruct;
}
}
return TestStruct;
}
After creating a child Blueprint class of SerializeActor, this is the Blueprint to save and load the Struct:
Hi:
Thanks for the very detailed answer, I really appreciate it. But please note one very important point, I am using my custom FArchive to do the serialization, which is different with the default one used by UGameplayStatics’s functions. My custom FArchive has set Ar.IsSaveGame to true, which leads to another way of the execution, so in that way the BP struct can’t be saved properly.
Please check this link link text, the 14 upvoted answer. My custom FArchive is like the FFortniteSaveGameArchive example, and with it I can’t save BP struct.
Hi, I’ve exactly the same problem. The issue seems to be that the SaveGame flag is not inherited from struct members.
I hope that behavior will be fixed in the future but in the meanwhile instead of structs I’m using classes that inherits from Object and containing only public members. That works fine and moreover it’s possible to set save / transient flags on each ‘class’ member.
If you are having an issue where the answer here isn’t related, please make a new post in Bug Reports and give detailed steps on how to reproduce it. Here is a guideline on how to write up an issue:
Coming back to this problem again this month;
For my projects I solved this issue by completely discarding use of FArchive classes and avoiding the Serialize() methods.
I’ve created my own serializer based on UProperty reflection + JSON to replace the ProxyArchive class.
Now I can finally read/write to structs created as Blueprint assets (user defined structs).
fixed in 2022 although since I’m here it might be worth to mention that each field of struct in it’s properties has to have ‘save game’ checked, setting it only on struct field is not enough.
For me this is the solution,Thanks,Whether you are defining a C++ structure or a blueprint structure, you need to check SaveGameFlag For MemberProperties that need to be saved. For the blueprint structure, click the button next to the MemberProperty of structure to expand the drop-down list. You can see the checkBox of SaveGameFlag. For the C++ structure, select the member properties that need to be saved. Just write SaveGame in UPROPERTY().