はじめに

「UE5でEpic Online Services(EOS)を使ってみる」の続きです。
ログインの実装が必要なので、未実装の方は準備編ログイン編をご確認ください。

プレーヤーデータはユーザー毎にストレージが用意されており、uint8の形式でファイルをアップロードできます。
セーブデータの保存などが主な用途になるかと思います。
今回はSaveGameの変換とアップロード・ダウンロードを実装します。

なお、今回はDeveloper Portalでの事前設定はありません。

目次

この章では、プレイヤーデータ機能に必要な実装を行っていきます。
必要なクラスを作成してC++を実装し、Blueprintで実行出来る状態にしてテストを行います。

  1. クラス作成
  2. C++
  3. Blueprint
  4. テスト

 

1.クラス作成

1-1.C++クラス
SaveGameの変換用のクラスを作成します。
BlueprintFunctionLibraryを親クラスにして、EOS_BlueprintFunctionをC++クラスで作成します。

アップロード・ダウンロード用のクラスを作成します。
OnlineBlueprintCallProxyBaseを親クラスに指定して、EOS_SetPlayerDataとEOS_GetPlayerDataをC++クラスで作成します。

1-2.Blueprintクラス
SaveGameのクラスを作成します。
SaveGameを親クラスに指定して、BP_SaveGameをBlueprintクラスで作成します。


 

2.C++

C++を以下で実装します。

EOS_BlueprintFunction.h(cppの編集はありません)

#pragma once

#include "CoreMinimal.h"
#include "Kismet/GameplayStatics.h"
#include "EOS_BlueprintFunction.generated.h"

UCLASS()
class EOS_API UEOS_BlueprintFunction : public UBlueprintFunctionLibrary
{
  GENERATED_BODY()

public:

  UFUNCTION(BlueprintPure, Category = "EOS")
  static USaveGame* int8ToSaveGame(const TArray<uint8>& DataToConvert)
  {
    return UGameplayStatics::LoadGameFromMemory(DataToConvert);
  }

  UFUNCTION(BlueprintPure, Category = "EOS")
  static TArray<uint8> SaveGameTouint8(USaveGame* DataToConvert)
  {
    TArray<uint8> Result;
    UGameplayStatics::SaveGameToMemory(DataToConvert, Result);
    return Result;
  }
};

EOS_SetPlayerData.h

#pragma once
#include "CoreMinimal.h"
#include "Net/OnlineBlueprintCallProxyBase.h"
#include "EOS_SetPlayerData.generated.h"

UCLASS()
class EOS_API UEOS_SetPlayerData : public UOnlineBlueprintCallProxyBase
{
  GENERATED_BODY()

  UPROPERTY(BlueprintAssignable)
  FEmptyOnlineDelegate OnSuccess;

  UPROPERTY(BlueprintAssignable)
  FEmptyOnlineDelegate OnFailure;

  UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"), Category = "EOS")
  static UEOS_SetPlayerData* SetPlayerData(UObject* WorldContextObject, FString FileName, TArray<uint8> UploadData);

  virtual void Activate() override;

private:
  void OnSetPlayerDataComplete(bool bWasSuccessful, const FUniqueNetId& UserID, const FString& var_FileName);

private:
  UWorld* World;
  TWeakObjectPtr<UObject> WorldContextObject;
  FString FileName;
  TArray<uint8> UploadData;
};

EOS_SetPlayerData.cpp

#include "OnlineSubsystemUtils.h"
#include "EOS_SetPlayerData.h"

UEOS_SetPlayerData* UEOS_SetPlayerData::SetPlayerData(UObject* WorldContextObject, FString FileName, TArray<uint8> UploadData)
{
  UEOS_SetPlayerData* PlayerData = NewObject<UEOS_SetPlayerData>();
  PlayerData->WorldContextObject = WorldContextObject;
  PlayerData->FileName = FileName;
  PlayerData->UploadData = UploadData;
  return PlayerData;
}

void UEOS_SetPlayerData::Activate()
{

  World = GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull);

  if (!World)
  {
    UE_LOG(LogTemp, Error, TEXT("SetPlayerData:Worldの取得に失敗"));
    OnFailure.Broadcast();
    return;
  }

  IOnlineSubsystem* SubsystemRef = Online::GetSubsystem(World);
  IOnlineIdentityPtr IdentityPointerRef = SubsystemRef->GetIdentityInterface();
  IOnlineUserCloudPtr UserCloudPointerRef = SubsystemRef->GetUserCloudInterface();

  if (UserCloudPointerRef)
  {
    const TSharedPtr<const FUniqueNetId> UserIDRef = IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef();
    UserCloudPointerRef->OnWriteUserFileCompleteDelegates.AddUObject(this, &UEOS_SetPlayerData::OnSetPlayerDataComplete);
    UserCloudPointerRef->WriteUserFile(*UserIDRef, FileName, UploadData);
    return;
  }

  OnFailure.Broadcast();
}

void UEOS_SetPlayerData::OnSetPlayerDataComplete(bool bWasSuccessful, const FUniqueNetId& UserID, const FString& var_FileName)
{
  if (bWasSuccessful)
  {
    UE_LOG(LogTemp, Warning, TEXT("Upload成功"));
    OnSuccess.Broadcast();
  }
  else
  {
    UE_LOG(LogTemp, Error, TEXT("Upload失敗"));
    OnFailure.Broadcast();
  }
}

EOS_GetPlayerData.h

#pragma once
#include "CoreMinimal.h"
#include "Net/OnlineBlueprintCallProxyBase.h"
#include "EOS_GetPlayerData.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetPlayerDataRetrieved, const TArray<uint8>&, DownloadData);

UCLASS()
class EOS_API UEOS_GetPlayerData : public UOnlineBlueprintCallProxyBase
{
  GENERATED_BODY()

  UPROPERTY(BlueprintAssignable)
  FOnGetPlayerDataRetrieved OnSuccess;

  UPROPERTY(BlueprintAssignable)
  FOnGetPlayerDataRetrieved OnFailure;

  UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"), Category = "EOS")
  static UEOS_GetPlayerData* GetPlayerData(UObject* WorldContextObject, FString FileName);

  virtual void Activate() override;

private:
  void OnGetPlayerDataComplete(bool bSuccess, const FUniqueNetId& UserID, const FString& var_FileName);

private:
  UWorld* World;
  TWeakObjectPtr<UObject> WorldContextObject;
  FString FileName;
  TArray<uint8> DownloadData;
};

EOS_GetPlayerData.cpp

#include "OnlineSubsystemUtils.h"
#include "EOS_GetPlayerData.h"


UEOS_GetPlayerData* UEOS_GetPlayerData::GetPlayerData(UObject* WorldContextObject, FString FileName)
{
  UEOS_GetPlayerData* PlayerData = NewObject<UEOS_GetPlayerData>();
  PlayerData->WorldContextObject = WorldContextObject;
  PlayerData->FileName = FileName;
  return PlayerData;
}

void UEOS_GetPlayerData::Activate()
{
  DownloadData.Empty();

  World = GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull);

  if (!World)
  {
    UE_LOG(LogTemp, Error, TEXT("GetPlayerData:Worldの取得に失敗"));
    OnFailure.Broadcast(DownloadData);
    return;
  }

  IOnlineSubsystem* SubsystemRef = Online::GetSubsystem(World);
  IOnlineIdentityPtr IdentityPointerRef = SubsystemRef->GetIdentityInterface();
  IOnlineUserCloudPtr UserCloudPointerRef = SubsystemRef->GetUserCloudInterface();

  if (UserCloudPointerRef)
  {
    const TSharedPtr<const FUniqueNetId> UserIDRef = IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef();
    UserCloudPointerRef->OnReadUserFileCompleteDelegates.AddUObject(this, &UEOS_GetPlayerData::OnGetPlayerDataComplete);
    UserCloudPointerRef->ReadUserFile(*UserIDRef, FileName);
    return;
  }

  OnFailure.Broadcast(DownloadData);
}

void UEOS_GetPlayerData::OnGetPlayerDataComplete(bool bWasSuccessful, const FUniqueNetId& UserID, const FString& var_FileName)
{
  // ポインターの取得
  IOnlineSubsystem* SubsystemRef = Online::GetSubsystem(World);
  IOnlineIdentityPtr IdentityPointerRef = SubsystemRef->GetIdentityInterface();
  IOnlineUserCloudPtr UserCloudPointerRef = SubsystemRef->GetUserCloudInterface();

  if (bWasSuccessful)
  {
    UE_LOG(LogTemp, Warning, TEXT("Download成功"));

    DownloadData.Empty();
    const TSharedPtr<const FUniqueNetId> UserIDRef = IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef();
    UserCloudPointerRef->GetFileContents(*UserIDRef, FileName, DownloadData);

    OnSuccess.Broadcast(DownloadData);
  }
  else
  {
    UE_LOG(LogTemp, Error, TEXT("Download失敗"));
    OnFailure.Broadcast(DownloadData);
  }
}

 

3.Blueprint

Blueprintを以下で実装します。

3-1.BP_SaveGame
変数RandomIntをInteger型で作成します。

3-2.EOS_Gameinstance
変数SaveGameをSaveGame型で作成します。

各カスタムイベントを用意し、以下で実装します。

Save:ランダムな値を生成し、SaveGameに保存します。

SetPlayerData:SaveGameをuin8に変換し、EOSへアップロードします。

・FileName:アップロードするファイル名を指定します。

GetPlayerData:EOSからファイルをダウンロードし、SaveGameに変換します。

・FileName:ダウンロードするファイル名を指定します。


 

4.テスト

4-1.SaveGame保存
ログイン後にSaveを実行し、保存した値を確認します。

4-2.アップロード
Save保存後にSetPlayerDataを実行し、ファイルをアップロードします。

4-2.ダウンロード
アップロード後にGetPlayerDataを実行し、ファイルをダウンロードします。
取得した値がアップロード時の値と一致していれば、SaveGameが無事にアップロード・ダウンロードされていることが確認できます。


 

5.Developer Portal

アップロードしたファイルはDeveloper Portal上で操作できます。

ゲームサービス>プレイヤーデータのストレージを開き、プレイヤーID(DevAuthToolから取得)を検索することで、そのユーザーのストレージを確認できます。

 

おわり

いかがでしたか。
以上で、EOSのお試し実装は終わりです。
とりあえず動く状態にするということで、メモリ管理やログインチェックの実装が出来ていてません。
アプリに組み込む際には実装を詰めていく必要がありそうです。