In most cases, you don’t need to save all the attributes in your AttributeSet. This is because many of them are set by GameplayEffects during the BeginPlay of the game. For instance, attributes like maximum health or stamina are typically determined by the character’s level and are applied consistently, whether it’s a new game or a loaded one.
However, some variable attributes, which are highly dependent on gameplay results, need to be saved and reloaded. For these attributes, the solution is relatively straightforward:
- Save them in a map of attribute names and values.
- On game load:
- Create a master GameplayEffect.
- For each retrieved name, find the related attribute.
- Add the attribute to the master GameplayEffect.
- Apply this effect to your character.
Here’s how you can implement this in code:
void AFirstPersonCharacter::Save(FActorSaveData& SaveData)
{
// Prepare memory writer and archive for serialization
FMemoryWriter MemWriter(SaveData.ByteData);
FObjectAndNameAsStringProxyArchive Ar(MemWriter, true);
Ar.ArIsSaveGame = true;
// Create a map to store attribute names and values
TMap<FString, float> SavedAttributes;
SavedAttributes.Add(AttributeSet->GetOxygenAttribute().GetName(), AttributeSet->GetOxygen());
SavedAttributes.Add(AttributeSet->GetUraniumOreAttribute().GetName(), AttributeSet->GetUraniumOre());
// Serialize the attributes map
Ar << SavedAttributes;
}
void AFirstPersonCharacter::Restore(FActorSaveData const& SaveData)
{
// Prepare memory reader and archive for deserialization
FMemoryReader MemReader(SaveData.ByteData);
FObjectAndNameAsStringProxyArchive Ar(MemReader, true);
Ar.ArIsSaveGame = true;
// Deserialize the attributes map
TMap<FString, float> SavedAttributes;
Ar << SavedAttributes;
// Create a GameplayEffect to restore the saved attributes
UGameplayEffect* GERestoreSavedData = NewObject<UGameplayEffect>(GetTransientPackage(), FName(TEXT("RestoreSavedData")));
// Get all attributes from the AttributeSet class
TArray<FGameplayAttribute> Attributes;
UAttributeSet::GetAttributesFromSetClass(AttributeSet->GetClass(), Attributes);
// Lambda to find an attribute by name
auto FindAttributeByName = [&Attributes](const FString& AttributeName) -> FGameplayAttribute
{
for (const FGameplayAttribute& Attribute : Attributes)
{
if (Attribute.GetName() == AttributeName)
{
return Attribute;
}
}
check(false); // Ensure the attribute is found; consider error management here
return FGameplayAttribute();
};
// Add modifications for each attribute in the map
for (const auto& AttributePair : SavedAttributes)
{
const FGameplayAttribute Attribute = FindAttributeByName(AttributePair.Key);
if (!Attribute.IsValid())
{
continue;
}
// Create a modifier for the attribute
int32 Idx = GERestoreSavedData->Modifiers.Num();
GERestoreSavedData->Modifiers.SetNum(Idx + 1);
FGameplayModifierInfo& ModInfo = GERestoreSavedData->Modifiers[Idx];
ModInfo.Attribute = Attribute;
ModInfo.ModifierOp = EGameplayModOp::Override;
ModInfo.ModifierMagnitude = FScalableFloat(AttributePair.Value);
}
// Apply the GameplayEffect to the AbilitySystemComponent
AbilitySystemComponent->ApplyGameplayEffectToSelf(
GERestoreSavedData,
1.0f,
AbilitySystemComponent->MakeEffectContext()
);
}
Leave a Reply