はじめに

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

統計データはユーザーが行った行動の統計情報を集計して、ユーザーごとに保持できます。
また、統計データごとに実績やリーダーボードが作成できます。

今回は、SCOREが加算されていく項目を作成します。

目次

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

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

 

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から確認できます。


おわり

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