はじめに
「UE5でEpic Online Services(EOS)を使ってみる」の続きです。
ログインの実装が必要なので、未実装の方は準備編・ログイン編をご確認ください。
プレーヤーデータはユーザー毎にストレージが用意されており、uint8の形式でファイルをアップロードできます。
セーブデータの保存などが主な用途になるかと思います。
今回はSaveGameの変換とアップロード・ダウンロードを実装します。
なお、今回はDeveloper Portalでの事前設定はありません。
目次
この章では、プレイヤーデータ機能に必要な実装を行っていきます。
必要なクラスを作成してC++を実装し、Blueprintで実行出来る状態にしてテストを行います。
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のお試し実装は終わりです。
とりあえず動く状態にするということで、メモリ管理やログインチェックの実装が出来ていてません。
アプリに組み込む際には実装を詰めていく必要がありそうです。