FMemoryWriter for Components

I’m attempting to use FMemoryWriter to write an actor’s data, is there any way to get the data of components as well? Does that happen automatically?

FMemoryWriter MemWriter(PlayerSaveData.ByteData);
FObjectAndNameAsStringProxyArchive Ar(MemWriter, true);
Ar.ArIsSaveGame = true;
MyPawn->Serialize(Ar);

This seems to capture some data from the actor, but I can’t seem to set my component’s data.

My player’s component is defined as such:

UCLASS()
class PROJECT_API APlayerCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	APlayerCharacter();
// ...
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Components")
	TObjectPtr<class UHealthComponent> HealthComponent;
};

The component’s data is defined as such:

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PROJECT_API UHealthComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UHealthComponent();

//...

public:

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, SaveGame, Category = "Health")
	float Health;

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, SaveGame, Category = "Health")
	float MaxHealth;
		
};

UHealthComponent::UHealthComponent()
{
	Health = 100.0f;
	MaxHealth = 100.0f;
}

FObjectAndNameAsStringProxyArchive serializes objects as string references so it won’t pull any data from components.
You can iterate components from the object yourself and serialize them one by one in a similar fashion.

Thanks Chatouille! Is there an efficient way to serialize each component? Will I have to put them into separate archives or can I combine them into one?

You can put them all into one, just make sure to store the amount of them beforehand so you know how many you have to read when loading. Also you might want to store an identifier to identify components as they may not always come out in the same order (using component name maybe, not sure if that’s good enough).
For example (pseudo code) :

// Writing
MyPawn->Serialize(Ar);
TArray<UActorComponent*> Components = MyPawn->GetAllComponents();
int32 Num = Components.Num();
Ar << Num;
for (auto Comp : Components)
{
    FString Name = Comp->GetName();
    Ar << Name;
    Comp->Serialize(Ar);
}

// Reading :
MyPawn->Serialize(Ar);
int32 Num;
Ar << Num;
for (int32 i=0; i<Num; i++)
{
    FString Name;
    Ar << Name;
    UActorComponent* Comp = MyPawn->FindComponentByName(Name);
    Comp->Serialize(Ar);
}

This is very barebones obviously and will go very wrong if one of the components is not found, or has changed class.
I can only recommend the standard SaveGame approaches, even if they feel more clunky to implement.