メインコンテンツまでスキップ

すべてを統合する - クラウドセーブ - (Unreal Engine モジュール)

Last updated on October 23, 2024

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.

  1. 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"))
  2. 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 being GAME_OPTIONS_KEY and the subcategory being SOUND_OPTIONS_KEY. It saves the music and sound effects (SFX) volume under the cloud save record which is represented with SOUND_OPTIONS_MUSIC_KEY and SOUND_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>
    }
  3. Use those record keys to load the game sound options from Cloud Save. Open the CloudSaveSubsystem_Starter class CPP file, navigate to OnLoadGameSoundOptions 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();
    })
    );
    }
  4. 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();
    }
    ));
    }
  5. Bind the OnLoadGameSoundOptions and OnSaveGameSoundOptions methods to when the options menu opens and closes, respectively. To do this, bind them to the OnOptionsWidgetActivated and OnOptionsWidgetDeactivated delegates from the options widget, which you can do by adding the following code to the BindDelegates method in the CloudSaveSubsystem_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);
    // ...
    }
  6. 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 the CloudSaveSubsystem_Starter class CPP file:

    void UCloudSaveSubsystem_Starter::UnbindDelegates()
    {
    UAuthEssentialsModels::OnLoginSuccessDelegate.RemoveAll(this);

    UOptionsWidget::OnOptionsWidgetActivated.RemoveAll(this);
    UOptionsWidget::OnOptionsWidgetDeactivated.RemoveAll(this);
    // ...
    }

Resources