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

Module 5: Unreal Engine - Use the Online Subsystem to store game options

Last updated on February 12, 2024

サブシステムのアンラップ


このセクションでは、クラウドセーブを実装してゲームの音楽と SFX の音量オプションを保存し、取得する方法を説明します。Byte Wars ゲームには、CloudSaveSubsystem クラスで定義されたゲームインスタンスサブシステムがあります。このサブシステムを使用すると、AccelByte クラウドセーブの任意のプレイヤーレコードを設定および取得できます。プレイヤーレコードは AccelByte クラウドセーブで利用可能なレコードタイプの 1 つです。

AccelByte クラウドセーブでサポートされているレコードタイプの定義は次のとおりです。

  • プレイヤーレコードはゲームのオプションと設定などのプレイヤーデータを保存するレコードタイプである。
  • ゲームレコードはゲームのお知らせやイベント設定などのゲームデータを保存するレコードタイプである。

次のセクションでは、CloudSaveSubsystem クラスと同様の関数を設定します。CloudSaveSubsystem_Starter クラスというスターターサブシステムを使用して、これらの関数を設定します。

スターターパックの内容


前のセクションで説明したように、AccelByte クラウドセーブにプレイヤーのゲームオプションを保存する CloudSaveSubsystem_Starter サブシステムクラスを設定します。CloudSaveSubsystem_Starter のファイルは、次のディレクトリにあります。

  • ヘッダーファイルは /Source/AccelByteWars/TutorialModules/Module-5/CloudSaveSubsystem_Starter.cpp にある。
  • cpp ファイルは /Source/AccelByteWars/TutorialModules/Module-5/CloudSaveSubsystem_Starter.cpp にある。

CloudSaveSubsystem_Starter クラスには、既に複数の関数が用意されています。CloudSaveSubsystem_Starter クラス cpp ファイルを開くと、Initialize() という関数があります。この関数には、CloudSaveInterface という変数を定義してあります。

void UCloudSaveSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);

// Get Online Subsystem and make sure it's valid.
const FOnlineSubsystemAccelByte* Subsystem = static_cast<const FOnlineSubsystemAccelByte*>(Online::GetSubsystem(GetWorld()));
if (!ensure(Subsystem))
{
UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("The online subsystem is invalid. Please make sure OnlineSubsystemAccelByte is enabled and DefaultPlatformService under [OnlineSubsystem] in the Engine.ini set to AccelByte."));
return;
}

// Grab the reference of the AccelByte Identity Interface and make sure it's valid.
CloudSaveInterface = StaticCastSharedPtr<FOnlineCloudSaveAccelByte>(Subsystem->GetCloudSaveInterface());
if (!ensure(CloudSaveInterface.IsValid()))
{
UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Cloud Save interface is not valid."));
return;
}

UTutorialModuleDataAsset* TutorialModule = UTutorialModuleUtility::GetTutorialModuleDataAsset(FPrimaryAssetId{ "TutorialModule:CLOUDSAVEESSENTIALS" }, this, true);
if (TutorialModule && TutorialModule->IsActiveAndDependenciesChecked() && TutorialModule->IsStarterMode())
{
BindDelegates();
}
}

CloudSaveInterfaceFOnlineCloudSaveAccelBytePtr インターフェイスタイプの変数です。FOnlineCloudSaveAccelBytePtr は AccelByte OSS の一部であり、このインターフェイスを利用して AccelByte クラウドセーブの関数にアクセスできます。次のセクションでは、CloudSaveInterface を使用して、ゲームオプションを保存および取得するためのプレイヤーレコードを設定および取得します。

AccelByte OSS を使用したクラウドセーブの実装


このセクションでは、AccelByte OSS を使用してクラウドセーブを実装し、ゲームオプションを保存および取得するためのプレイヤーレコードを設定および取得します。

  1. CloudSaveSubsystem_Starter クラスヘッダーファイルを開き、次の関数を宣言しましょう。

    public:
    void SetPlayerRecord(const APlayerController* PC, const FString& RecordKey, const FJsonObject& RecordData, const FOnSetCloudSaveRecordComplete& OnSetRecordComplete);
    void GetPlayerRecord(const APlayerController* PC, const FString& RecordKey, const FOnGetCloudSaveRecordComplete& OnGetRecordComplete);
    void DeletePlayerRecord(const APlayerController* PC, const FString& RecordKey, const FOnDeleteCloudSaveRecordComplete& OnDeleteRecordComplete);

    private:
    void OnSetPlayerRecordComplete(int32 LocalUserNum, bool bWasSuccessful, const FString& Error, const FOnSetCloudSaveRecordComplete OnSetRecordComplete);
    void OnGetPlayerRecordComplete(int32 LocalUserNum, bool bWasSuccessful, const FAccelByteModelsUserRecord& UserRecord, const FString& Error, const FOnGetCloudSaveRecordComplete OnGetRecordComplete);
    void OnDeletePlayerRecordComplete(int32 LocalUserNum, bool bWasSuccessful, const FString& Error, const FOnDeleteCloudSaveRecordComplete OnDeleteRecordComplete);
  2. 上記の関数宣言の内容は一目瞭然です。SetPlayerRecord() はクラウドセーブにプレイヤーレコードを設定するため、GetPlayerRecord() はクラウドセーブからプレイヤーレコードを取得するため、DeletePlayerRecord() はクラウドセーブからプレイヤーレコードを削除するために使用します。クラウドセーブのデータの設定と取得は非同期プロセスであるため、コールバックとして機能する関数が必要です。この場合、それぞれのコールバック関数は、OnSetPlayerRecordComplete() 関数、OnGetPlayerRecordComplete() 関数、OnDeletePlayerRecordComplete() 関数です。

  3. 次に、関数定義も作成しましょう。まずは、SetPlayerRecord() 関数です。CloudSaveSubsystem_Starter クラス cpp ファイルを開き、次のコードを追加します。以下のコードは、プレイヤーレコードデータを設定するリクエストを送信します。プレイヤーレコードを設定するには、JSON 形式で保存するレコードキーとレコードデータを定義する必要があります。次に、クラウドセーブはプレイヤーレコードを作成するか、既に存在する場合は更新します。リクエストが完了すると、OnSetPlayerRecordComplete() が呼び出されます。

    void UCloudSaveSubsystem_Starter::SetPlayerRecord(const APlayerController* PC, const FString& RecordKey, const FJsonObject& RecordData, const FOnSetCloudSaveRecordComplete& OnSetRecordComplete)
    {
    if (!ensure(CloudSaveInterface.IsValid()))
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Cloud Save interface is not valid."));
    return;
    }

    const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
    ensure(LocalPlayer != nullptr);
    int32 LocalUserNum = LocalPlayer->GetControllerId();

    // Create Player Record or update it if it already exists.
    OnSetPlayerRecordCompletedDelegateHandle = CloudSaveInterface->AddOnReplaceUserRecordCompletedDelegate_Handle(LocalUserNum, FOnReplaceUserRecordCompletedDelegate::CreateUObject(this, &ThisClass::OnSetPlayerRecordComplete, OnSetRecordComplete));
    CloudSaveInterface->ReplaceUserRecord(LocalUserNum, RecordKey, RecordData);
    }
  4. 次に、OnSetPlayerRecordComplete() 関数を定義しましょう。cpp ファイルに、次のコードを追加します。以下のコードは OnSetRecordComplete という別のコールバックを実行して、プレイヤーにプロセスが正常に行われたかどうかを知らせます。このコールバックは、後ほど次のセクションで利用します。

    void UCloudSaveSubsystem_Starter::OnSetPlayerRecordComplete(int32 LocalUserNum, bool bWasSuccessful, const FString& Error, const FOnSetCloudSaveRecordComplete OnSetRecordComplete)
    {
    if (bWasSuccessful)
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Log, TEXT("Success to set player record."));
    }
    else
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Log, TEXT("Failed to set player record. Message: %s"), *Error);
    }

    CloudSaveInterface->ClearOnReplaceUserRecordCompletedDelegate_Handle(LocalUserNum, OnSetPlayerRecordCompletedDelegateHandle);
    OnSetRecordComplete.ExecuteIfBound(bWasSuccessful);
    }
  5. それでは、GetPlayerRecord() 関数の関数定義を作成します。cpp ファイルに、次のコードを追加します。以下のコードは、プレイヤーレコードデータを取得するリクエストを送信します。データの設定と同様に、プレイヤーレコードを取得するには、レコードキーを定義する必要があります。リクエストが完了すると、OnGetPlayerRecordComplete() が呼び出されます。

    void UCloudSaveSubsystem_Starter::GetPlayerRecord(const APlayerController* PC, const FString& RecordKey, const FOnGetCloudSaveRecordComplete& OnGetRecordComplete)
    {
    if (!ensure(CloudSaveInterface.IsValid()))
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Cloud Save interface is not valid."));
    return;
    }

    const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
    ensure(LocalPlayer != nullptr);
    int32 LocalUserNum = LocalPlayer->GetControllerId();

    OnGetPlayerRecordCompletedDelegateHandle = CloudSaveInterface->AddOnGetUserRecordCompletedDelegate_Handle(LocalUserNum, FOnGetUserRecordCompletedDelegate::CreateUObject(this, &ThisClass::OnGetPlayerRecordComplete, OnGetRecordComplete));
    CloudSaveInterface->GetUserRecord(LocalUserNum, RecordKey);
    }
  6. 次に、OnGetPlayerRecordComplete() 関数を定義します。cpp ファイルに、次のコードを追加します。データの設定と同様に、以下のコードは OnGetRecordComplete という別のコールバックを実行して、プレイヤーにプロセスが正常に行われたかどうかを知らせます。このコールバックは、JSON 形式で取得したプレイヤーレコードも送信します。このコールバックは、後ほど次のセクションで利用します。

    void UCloudSaveSubsystem_Starter::OnGetPlayerRecordComplete(int32 LocalUserNum, bool bWasSuccessful, const FAccelByteModelsUserRecord& UserRecord, const FString& Error, const FOnGetCloudSaveRecordComplete OnGetRecordComplete)
    {
    FJsonObject RecordResult;

    if (bWasSuccessful)
    {
    RecordResult = UserRecord.Value.JsonObject.ToSharedRef().Get();
    UE_LOG_CLOUDSAVE_ESSENTIALS(Log, TEXT("Success to get player record."));
    }
    else
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Log, TEXT("Failed to get player record. Message: %s"), *Error);
    }

    CloudSaveInterface->ClearOnGetUserRecordCompletedDelegate_Handle(LocalUserNum, OnGetPlayerRecordCompletedDelegateHandle);
    OnGetRecordComplete.ExecuteIfBound(bWasSuccessful, RecordResult);
    }
  7. DeletePlayerRecord() 関数の関数定義を作成しましょう。cpp ファイルに、次のコードを追加します。以下のコードは、プレイヤーレコードデータを削除するリクエストを送信します。前述の関数と同様に、プレイヤーレコードを削除するには、削除するレコードキーを定義する必要があります。リクエストが完了すると、OnDeletePlayerRecordComplete() が呼び出されます。

    void UCloudSaveSubsystem_Starter::DeletePlayerRecord(const APlayerController* PC, const FString& RecordKey, const FOnDeleteCloudSaveRecordComplete& OnDeleteRecordComplete)
    {
    if (!ensure(CloudSaveInterface.IsValid()))
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Warning, TEXT("Cloud Save interface is not valid."));
    return;
    }

    const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
    ensure(LocalPlayer != nullptr);
    int32 LocalUserNum = LocalPlayer->GetControllerId();

    OnDeletePlayerRecordCompletedDelegateHandle = CloudSaveInterface->AddOnDeleteUserRecordCompletedDelegate_Handle(LocalUserNum, FOnDeleteUserRecordCompletedDelegate::CreateUObject(this, &ThisClass::OnDeletePlayerRecordComplete, OnDeleteRecordComplete));
    CloudSaveInterface->DeleteUserRecord(LocalUserNum, RecordKey);
    }
  8. 次に、OnDeletePlayerRecordComplete() 関数を定義しましょう。cpp ファイルに、次のコードを追加します。先ほどのコールバック関数と同様に、以下のコードは OnDeleteRecordComplete という別のコールバックを実行して、プレイヤーにレコードが正常に削除されたかどうかを知らせます。

    void UCloudSaveSubsystem_Starter::OnDeletePlayerRecordComplete(int32 LocalUserNum, bool bWasSuccessful, const FString& Error, const FOnDeleteCloudSaveRecordComplete OnDeleteRecordComplete)
    {
    if (bWasSuccessful)
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Log, TEXT("Success to delete player record."));
    }
    else
    {
    UE_LOG_CLOUDSAVE_ESSENTIALS(Log, TEXT("Failed to delete player record. Message: %s"), *Error);
    }

    CloudSaveInterface->ClearOnDeleteUserRecordCompletedDelegate_Handle(LocalUserNum, OnDeletePlayerRecordCompletedDelegateHandle);
    OnDeleteRecordComplete.ExecuteIfBound(bWasSuccessful);
    }
  9. お疲れさまでした。CloudSaveSubsystem_Starter が完成しました。次のセクションでは、このサブシステムを使用してクラウドセーブのゲームオプションを設定し、取得します。

リソース