Put it all together - Cloud save - (Unreal Engine module)
Connect the UI to save to and load from cloud save
In this section, you will learn how to connect the options menu to save and load the game sound options using AccelByte Gaming Services (AGS) Cloud Save.
Define keys for the records you intend to save and load. In the Byte Wars project, these record keys defined in the
/Source/AccelByteWars/TutorialModules/Storage/CloudSaveEssentials/CloudSaveModels.h
Header. Review it and continue to the next step.#define GAME_OPTIONS_KEY FString(TEXT("GameOptions"))
#define SOUND_OPTIONS_KEY FString(TEXT("Sound"))
#define SOUND_OPTIONS_MUSIC_KEY FString(TEXT("musicvolume"))
#define SOUND_OPTIONS_SFX_KEY FString(TEXT("sfxvolume"))A Cloud Save record is stored as a pair of string keys and JSON object values. The JSON object values are a collection of key and value pairs. Byte Wars uses the
category-subcategory
format as the key, with the category beingGAME_OPTIONS_KEY
and the subcategory beingSOUND_OPTIONS_KEY
. It saves the music and sound effects (SFX) volume under the cloud save record which is represented withSOUND_OPTIONS_MUSIC_KEY
andSOUND_OPTIONS_SFX_KEY
in the record's JSON fields. A basic hierarchy would look something like the code below. Again, you don't need to do anything in this step, so review it and proceed.{
GameOptions-Sound:
"musicvolume": <value>,
"sfxvolume": <value>
}Use those record keys to load the game sound options from Cloud Save. Open the
CloudSaveSubsystem_Starter
class CPP file, navigate toOnLoadGameSoundOptions
function, and add the code below. This will show a loading screen and send a request to get the game options from Cloud Save. Once the request is completed, it then hides the loading screen and updates the game options based on the response from Cloud Save.void UCloudSaveSubsystem_Starter::OnLoadGameSoundOptions(const APlayerController* PlayerController, TDelegate<void()> OnComplete)
{
if (!PlayerController)
{
UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Cannot get game options from Cloud Save. Player Controller is null."));
return;
}
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
ensure(GameInstance);
UPromptSubsystem* PromptSubsystem = GameInstance->GetSubsystem<UPromptSubsystem>();
ensure(PromptSubsystem);
PromptSubsystem->ShowLoading();
// Get game options from Cloud Save.
GetPlayerRecord(
PlayerController,
FString::Printf(TEXT("%s-%s"), *GAME_OPTIONS_KEY, *SOUND_OPTIONS_KEY),
FOnGetCloudSaveRecordComplete::CreateWeakLambda(this, [this, GameInstance, PromptSubsystem, OnComplete, PlayerController](bool bWasSuccessful, FJsonObject& Result)
{
UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Get game options from Cloud Save was successful: %s"), bWasSuccessful ? TEXT("True") : TEXT("False"));
PromptSubsystem->HideLoading();
// Update the local game options based on the Cloud Save record.
double MusicVolume = 0.0f, SFXVolume = 0.0f;
if (Result.TryGetNumberField(SOUND_OPTIONS_MUSIC_KEY, MusicVolume))
{
GameInstance->SetMusicVolume(MusicVolume);
}
if (Result.TryGetNumberField(SOUND_OPTIONS_SFX_KEY, SFXVolume))
{
GameInstance->SetSFXVolume(SFXVolume);
}
GameInstance->SaveGameSettings(GetLocalUserIndex(PlayerController));
OnComplete.ExecuteIfBound();
})
);
}Save the sound options to Cloud Save. Navigate to the definition of the
OnSaveGameSoundOptions
method in the class CPP file and add the code below. This will show a loading screen and send a request to set the game options in the Cloud Save record. Once the request completes, it then hides the loading screen.void UCloudSaveSubsystem_Starter::OnSaveGameSoundOptions(const APlayerController* PlayerController, TDelegate<void()> OnComplete)
{
if (!PlayerController)
{
UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Cannot set game options from Cloud Save. Player Controller is null."));
return;
}
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
ensure(GameInstance);
UPromptSubsystem* PromptSubsystem = GameInstance->GetSubsystem<UPromptSubsystem>();
ensure(PromptSubsystem);
PromptSubsystem->ShowLoading(LOCTEXT("Saving", "Saving"));
// Construct game options to save.
FJsonObject GameOptionsData;
GameOptionsData.SetNumberField(SOUND_OPTIONS_MUSIC_KEY, GameInstance->GetMusicVolume());
GameOptionsData.SetNumberField(SOUND_OPTIONS_SFX_KEY, GameInstance->GetSFXVolume());
// Save the game options to Cloud Save.
SetPlayerRecord(
PlayerController,
FString::Printf(TEXT("%s-%s"), *GAME_OPTIONS_KEY, *SOUND_OPTIONS_KEY),
GameOptionsData,
FOnSetCloudSaveRecordComplete::CreateWeakLambda(this, [this, PromptSubsystem, OnComplete](bool bWasSuccessful)
{
UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Set game options from Cloud Save was successful: %s"), bWasSuccessful ? TEXT("True") : TEXT("False"));
PromptSubsystem->HideLoading();
OnComplete.ExecuteIfBound();
}
));
}Bind the
OnLoadGameSoundOptions
andOnSaveGameSoundOptions
methods to when the options menu opens and closes, respectively. To do this, bind them to theOnOptionsWidgetActivated
andOnOptionsWidgetDeactivated
delegates from the options widget, which you can do by adding the following code to theBindDelegates
method in theCloudSaveSubsystem_Starter
class CPP file:void UCloudSaveSubsystem_Starter::BindDelegates()
{
UAuthEssentialsModels::OnLoginSuccessDelegate.AddUObject(this, &ThisClass::OnLoadGameSoundOptions, TDelegate<void()>());
// ...
UOptionsWidget::OnOptionsWidgetActivated.AddUObject(this, &ThisClass::OnLoadGameSoundOptions);
UOptionsWidget::OnOptionsWidgetDeactivated.AddUObject(this, &ThisClass::OnSaveGameSoundOptions);
// ...
}Unbind the delegates mentioned above when the cloud save subsystem is destroyed. This can be done with the following code in the
UnbindDelegates
method in theCloudSaveSubsystem_Starter
class CPP file:void UCloudSaveSubsystem_Starter::UnbindDelegates()
{
UAuthEssentialsModels::OnLoginSuccessDelegate.RemoveAll(this);
UOptionsWidget::OnOptionsWidgetActivated.RemoveAll(this);
UOptionsWidget::OnOptionsWidgetDeactivated.RemoveAll(this);
// ...
}
Resources
- The files used in this tutorial section are available in the Unreal Byte Wars GitHub repository.
- AccelByteWars/Source/AccelByteWars/TutorialModules/Storage/CloudSaveEssentials/CloudSaveModels.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Storage/CloudSaveEssentials/CloudSaveSubsystem_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Storage/CloudSaveEssentials/CloudSaveSubsystem_Starter.cpp