はじめに
「UE5でEpic Online Services(EOS)を使ってみる」の続きです。
ログインの実装が必要なので、未実装の方は準備編・ログイン編をご確認ください。
統計データはユーザーが行った行動の統計情報を集計して、ユーザーごとに保持できます。
また、統計データごとに実績やリーダーボードが作成できます。
今回は、SCOREが加算されていく項目を作成します。
目次
この章では、統計データ機能に必要な実装を行っていきます。
ポータルサイトにて統計データに必要な設定を行い、
必要なクラスを作成してC++を実装し、Blueprintで実行出来る状態にしてテストを行います。
1.Developer Portal
Developer Portalで値を格納するための項目を作成します。
ゲームサービス>統計データから統計を作成を開き、統計データ名をSCORE_SUM、集計の種類にSUMを指定します。
統計データ名は全て大文字にします。
EOS_OSSプラグイン上では、名称は大文字と小文字を区別しないFNameが使用されるため、大文字にする必要があるそうです。


2.クラス作成
更新・取得用のクラスを作成します。
OnlineBlueprintCallProxyBaseを親クラスに指定して、EOS_SetStatsとEOS_GetStatsをC++クラスで作成します。
3.C++
C++を以下で実装します。
EOS_DataDefinition.h(以下を追加)
USTRUCT(BlueprintType)
struct FStatData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString StatName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Value;
};
EOS_SetStats.h
#pragma once
#include "CoreMinimal.h"
#include "Net/OnlineBlueprintCallProxyBase.h"
#include "Interfaces/OnlineStatsInterface.h"
#include "EOS_SetStats.generated.h"
UCLASS()
class EOS_API UEOS_SetStats : public UOnlineBlueprintCallProxyBase
{
GENERATED_BODY()
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"), Category = "EOS")
static UEOS_SetStats* SetStats(UObject* WorldContextObject, FString StatName, int32 StatValue);
virtual void Activate() override;
private:
void OnSetStatsComplete(const FOnlineError& Result);
private:
UWorld* World;
TWeakObjectPtr<UObject> WorldContextObject;
FString StatName;
int32 StatValue;
};
EOS_SetStats.cpp
#include "OnlineSubsystemUtils.h"
#include "EOS_SetStats.h"
UEOS_SetStats* UEOS_SetStats::SetStats(UObject* WorldContextObject,FString StatName, int32 StatValue)
{
UEOS_SetStats* Stats = NewObject<UEOS_SetStats>();
Stats->WorldContextObject = WorldContextObject;
Stats->StatName = StatName;
Stats->StatValue = StatValue;
return Stats;
}
void UEOS_SetStats::Activate()
{
World = GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull);
if (!World)
{
UE_LOG(LogTemp, Error, TEXT("UpdateStat:Worldの取得に失敗"));
OnFailure.Broadcast();
return;
}
IOnlineSubsystem* SubsystemRef = Online::GetSubsystem(World);
IOnlineIdentityPtr IdentityPointerRef = SubsystemRef->GetIdentityInterface();
IOnlineStatsPtr StatsPointerRef = SubsystemRef->GetStatsInterface();
FOnlineStatsUserUpdatedStats StatVar = FOnlineStatsUserUpdatedStats(IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef());
StatVar.Stats.Add(StatName, FOnlineStatUpdate(StatValue, FOnlineStatUpdate::EOnlineStatModificationType::Unknown));
TArray<FOnlineStatsUserUpdatedStats> StatArray;
StatArray.Add(StatVar);
if (StatsPointerRef)
{
StatsPointerRef->UpdateStats(
IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef(),
StatArray,
FOnlineStatsUpdateStatsComplete::CreateUObject(this, &UEOS_SetStats::OnSetStatsComplete)
);
return;
}
OnFailure.Broadcast();
}
void UEOS_SetStats::OnSetStatsComplete(const FOnlineError& Result)
{
if (Result == FOnlineError::Success())
{
UE_LOG(LogTemp, Warning, TEXT("Stat更新成功"));
OnSuccess.Broadcast();
}
else
{
UE_LOG(LogTemp, Error, TEXT("Stat更新失敗"));
OnFailure.Broadcast();
}
}
UEOS_GetStats.h
#pragma once
#include "CoreMinimal.h"
#include "Net/OnlineBlueprintCallProxyBase.h"
#include "Interfaces/OnlineStatsInterface.h"
#include "EOS_DataDefinition.h"
#include "EOS_GetStats.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStatsRetrieved, const TArray<FStatData>&, Stats);
UCLASS()
class EOS_API UEOS_GetStats : public UOnlineBlueprintCallProxyBase
{
GENERATED_BODY()
UPROPERTY(BlueprintAssignable)
FOnStatsRetrieved OnSuccess;
UPROPERTY(BlueprintAssignable)
FOnStatsRetrieved OnFailure;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"), Category = "EOS")
static UEOS_GetStats* GetStats(UObject* WorldContextObject, TArray<FString> StatNames);
virtual void Activate() override;
private:
void OnGetStatsComplete(const FOnlineError& ResultState, const TArray<TSharedRef<const FOnlineStatsUserStats>>& UserStats);
private:
UWorld* World;
TWeakObjectPtr<UObject> WorldContextObject;
TArray<FString> StatNames;
TArray<FStatData> Stats;
};
UEOS_GetStats.cpp
#include "OnlineSubsystemUtils.h"
#include "EOS_GetStats.h"
UEOS_GetStats* UEOS_GetStats::GetStats(UObject* WorldContextObject,TArray<FString> StatNames)
{
UEOS_GetStats* Stats = NewObject<UEOS_GetStats>();
Stats->WorldContextObject = WorldContextObject;
Stats->StatNames = StatNames;
return Stats;
}
void UEOS_GetStats::Activate()
{
Stats.Empty();
World = GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull);
if (!World)
{
UE_LOG(LogTemp, Error, TEXT("GetStat:Worldの取得に失敗"));
OnFailure.Broadcast(Stats);
return;
}
IOnlineSubsystem* SubsystemRef = Online::GetSubsystem(World);
IOnlineIdentityPtr IdentityPointerRef = SubsystemRef->GetIdentityInterface();
IOnlineStatsPtr StatsPointerRef = SubsystemRef->GetStatsInterface();
TArray<TSharedRef<const FUniqueNetId>> Users;
Users.Add(IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef());
if (StatsPointerRef)
{
StatsPointerRef->QueryStats(
IdentityPointerRef->GetUniquePlayerId(0).ToSharedRef(),
Users,
StatNames,
FOnlineStatsQueryUsersStatsComplete::CreateUObject(this, &UEOS_GetStats::OnGetStatsComplete)
);
return;
}
OnFailure.Broadcast(Stats);
}
void UEOS_GetStats::OnGetStatsComplete(const FOnlineError& ResultState, const TArray<TSharedRef<const FOnlineStatsUserStats>>& UserStats)
{
if (ResultState.WasSuccessful())
{
Stats.Empty();
for (auto StatsVar : UserStats)
{
for (auto StoredValueRef : StatsVar->Stats)
{
FString Keyname = StoredValueRef.Key;
int32 Value;
StoredValueRef.Value.GetValue(Value);
FStatData StatData;
StatData.StatName = Keyname;
StatData.Value = Value;
Stats.Add(StatData);
UE_LOG(LogTemp, Warning, TEXT("Stats名:%s 値:%s"), *Keyname, *FString::FromInt(Value));
}
}
OnSuccess.Broadcast(Stats);
}
else
{
UE_LOG(LogTemp, Error, TEXT("Stats取得失敗 - %s"), *ResultState.ToLogString());
OnFailure.Broadcast(Stats);
}
}
4.Blueprint
Blueprintを以下で実装します。
EOS_Gameinstanceに各カスタムイベントを用意し、以下で実装します。
SetStats:指定された統計データに入力された値を送信します。

GetStats:指定された統計データを取得します。複数の統計データを一度に取得することができます。

5.テスト
5-1.更新
ログイン後、SetStatsを実行します。

5-2.取得
GetStatsを実行します。

5-3.再度更新
SetStatsとGetStatsを交互に実行します。
値が増えているならば、統計データの更新と取得が問題なく行われていることが確認できます。

5-4.Developer Portal
ゲームサービス>統計データから「プレーヤーの統計データをリセットする」を開き、プレーヤー検索画面でユーザーを検索します。
プレーヤーが保持している値を確認でき、値のリセットが行えます。
プレーヤーカウントIDはDevAuthToolから確認できます。

おわり
お疲れ様です。
以上で統計データ編は終わりです、次はリーダーボード編です。