OSS を使用してリーダーボードを表示する - 全期間のリーダーボード - (Unreal Engine モジュール)
注釈:本資料はAI技術を用いて翻訳されています。
サブシステムの展開
このチュートリアルでは、AccelByte Gaming Services (AGS) Online Subsystem (OSS) を使用してリーダーボードを取得する方法を学びます。Byte Wars プロジェクトには、LeaderboardSubsystem という名前の Game Instance Subsystem がすでに作成されています。このサブシステムには、基本的なリーダーボード関連の機能が含まれています。このチュートリアルでは、そのサブシステムのスターター版を使用して、リーダーボード機能をゼロから実装します。
スターターパックの内容
このチュートリアルに従うために、LeaderboardSubsystem_Starter という名前のスターターサブシステムクラスが用意されています。このクラスは リソース セクションで入手でき、以下のファイルで構成されています。
- Header file:
/Source/AccelByteWars/TutorialModules/Engagement/LeaderboardEssentials/LeaderboardSubsystem_Starter.h - CPP file:
/Source/AccelByteWars/TutorialModules/Engagement/LeaderboardEssentials/LeaderboardSubsystem_Starter.cpp
LeaderboardSubsystem_Starter クラスには、いくつかの機能が提供されています。
-
LeaderboardInterfaceとUserInterfaceという名前の AGS OSS インターフェース宣言。これらのインターフェースを使用して、後でリーダーボード関連の機能を実装します。protected:
// ...
FOnlineUserAccelBytePtr UserInterface;
FOnlineLeaderboardAccelBytePtr LeaderboardInterface; -
PlayerControllerからUniqueNetIdを取得するヘルパー関数。上記の AGS OSS インターフェースを使用するために、このヘルパーが必要になります。FUniqueNetIdPtr ULeaderboardSubsystem_Starter::GetUniqueNetIdFromPlayerController(const APlayerController* PC) const
{
if (!ensure(PC))
{
return nullptr;
}
ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
return nullptr;
}
return LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId();
} -
PlayerControllerからLocalUserNumを取得するヘルパー関数。AGS OSS インターフェースを使用するために、このヘルパーも必要になります。int32 ULeaderboardSubsystem_Starter::GetLocalUserNumFromPlayerController(const APlayerController* PC) const
{
if (!PC)
{
return INDEX_NONE;
}
const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
if (!LocalPlayer)
{
return INDEX_NONE;
}
return LocalPlayer->GetControllerId();
}
スターターサブシステムに加えて、/Source/AccelByteWars/TutorialModules/Engagement/LeaderboardEssentials/LeaderboardEssentialsModels.h ファイルには、いくつかの定数、デリゲート、その他のヘルパーも用意されています。そのファイルには、以下のヘルパーがあります。
-
LeaderboardRankという名前のヘルパークラス。表示名、ランク、スコアなど、個々のプレイヤーのランク情報が含まれています。後でリーダーボードエントリを表示するために、これが必要になります。UCLASS()
class ACCELBYTEWARS_API ULeaderboardRank : public UObject
{
GENERATED_BODY()
public:
FUniqueNetIdRepl UserId;
int32 Rank;
FString DisplayName;
float Score;
void Init(const FUniqueNetIdRepl InUserId, const int32 InRank, const FString InDisplayName, const float InScore)
{
UserId = InUserId;
Rank = InRank;
DisplayName = InDisplayName;
Score = InScore;
}
}; -
リーダーボードデータの取得プロセスが完了したときにコールバックとして使用できるデリゲート。
DECLARE_DELEGATE_TwoParams(FOnGetLeaderboardRankingComplete, bool /*bWasSuccessful*/, const TArray<ULeaderboardRank*> /*Rankings*/);
リーダーボードランキングの取得
このセクションでは、リーダーボードランキングを取得する機能を実装します。
-
LeaderboardSubsystem_Starterクラスのヘッダーファイルを開き、以下の関数を宣言します。public:
// ...
void GetRankings(const APlayerController* PC, const FString& LeaderboardCode, const int32 ResultLimit, const FOnGetLeaderboardRankingComplete& OnComplete = FOnGetLeaderboardRankingComplete()); -
リーダーボードランキングの取得プロセスが完了したときに処理するコールバック関数を宣言します。
protected:
// ...
void OnGetRankingsComplete(bool bWasSuccessful, const int32 LocalUserNum, const FOnlineLeaderboardReadRef LeaderboardObj, const FOnGetLeaderboardRankingComplete OnComplete); -
リーダーボードリストからユーザー情報をクエリするコールバック関数を宣言します。
protected:
// ...
void OnQueryUserInfoComplete(
const FOnlineError& Error,
const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo,
const int32 LocalUserNum,
const FOnlineLeaderboardReadRef LeaderboardObj,
const FOnGetLeaderboardRankingComplete OnComplete); -
上記の関数を定義します。
LeaderboardSubsystem_Starterクラスの CPP ファイルを開き、まずGetRankings()関数を定義します。この関数は、定義された範囲内のリーダーボードランキングを取得するリクエストを送信します。この場合、ランク 0 から特定のランク制限までのリーダーボードを取得します。完了すると、OnGetRankingsComplete()関数を呼び出してコールバックを処理します。void ULeaderboardSubsystem_Starter::GetRankings(const APlayerController* PC, const FString& LeaderboardCode, const int32 ResultLimit, const FOnGetLeaderboardRankingComplete& OnComplete)
{
if (!ensure(LeaderboardInterface.IsValid()) || !ensure(UserInterface.IsValid()))
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Cannot get leaderboard rankings. Leaderboard Interface or User Interface is not valid."));
return;
}
if (!ensure(PC))
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Cannot get leaderboard rankings. PlayerController is null."));
return;
}
const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
FOnlineLeaderboardReadRef LeaderboardObj = MakeShared<FOnlineLeaderboardRead, ESPMode::ThreadSafe>();
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
LeaderboardObj->LeaderboardName = LeaderboardCode;
#else
LeaderboardObj->LeaderboardName = FName(LeaderboardCode);
#endif
// Get the leaderboard within the range of 0 to ResultLimit.
OnLeaderboardReadCompleteDelegateHandle = LeaderboardInterface->AddOnLeaderboardReadCompleteDelegate_Handle(FOnLeaderboardReadCompleteDelegate::CreateUObject(this, &ThisClass::OnGetRankingsComplete, LocalUserNum, LeaderboardObj, OnComplete));
LeaderboardInterface->ReadLeaderboardsAroundRank(0, ResultLimit, LeaderboardObj);
} -
OnGetRankingsComplete()関数とOnQueryUserInfoComplete()関数を定義します。リーダーボードランキングの取得リクエストプロセスが完了すると、リーダーボードメンバーのユーザー ID とそのスコアポイントのリストが返されます。各メンバーのユーザー情報(例:表示名)を取得するには、それらをクエリする必要があります。ユーザー情報がクエリされると、この関数は割り当てられたコールバックにリーダーボードランキングのリストを返します。void ULeaderboardSubsystem_Starter::OnGetRankingsComplete(bool bWasSuccessful, const int32 LocalUserNum, const FOnlineLeaderboardReadRef LeaderboardObj, const FOnGetLeaderboardRankingComplete OnComplete)
{
ensure(UserInterface);
ensure(LeaderboardInterface);
LeaderboardInterface->ClearOnLeaderboardReadCompleteDelegate_Handle(OnLeaderboardReadCompleteDelegateHandle);
if (!bWasSuccessful)
{
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Failed to get leaderboard rankings with code: %s"), *LeaderboardObj->LeaderboardName);
#else
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Failed to get leaderboard rankings with code: %s"), *LeaderboardObj->LeaderboardName.ToString());
#endif
OnComplete.ExecuteIfBound(false, TArray<ULeaderboardRank*>());
return;
}
// Collect leaderboard members' player id.
TPartyMemberArray LeaderboardMembers;
for (const FOnlineStatsRow& Row : LeaderboardObj->Rows)
{
if (Row.PlayerId.IsValid())
{
LeaderboardMembers.Add(Row.PlayerId->AsShared());
}
}
// Query leaderboard members' user information.
if (UStartupSubsystem* StartupSubsystem = GetGameInstance()->GetSubsystem<UStartupSubsystem>())
{
StartupSubsystem->QueryUserInfo(
LocalUserNum,
LeaderboardMembers,
FOnQueryUsersInfoCompleteDelegate::CreateUObject(this, &ThisClass::OnQueryUserInfoComplete, LocalUserNum, LeaderboardObj, OnComplete));
}
else
{
OnComplete.ExecuteIfBound(false, TArray<ULeaderboardRank*>());
}
}void ULeaderboardSubsystem_Starter::OnQueryUserInfoComplete(
const FOnlineError& Error,
const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo,
const int32 LocalUserNum,
const FOnlineLeaderboardReadRef LeaderboardObj,
const FOnGetLeaderboardRankingComplete OnComplete)
{
if (!ensure(UserInterface))
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Cannot get leaderboard. User Interface is not valid."));
return;
}
if (!Error.bSucceeded)
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Failed to get leaderboard with code: %s. Error: %s"), *Error.ErrorCode, *Error.ErrorMessage.ToString());
OnComplete.ExecuteIfBound(false, TArray<ULeaderboardRank*>());
return;
}
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Success in getting the leaderboard rankings with code: %s"), *LeaderboardObj->LeaderboardName);
#else
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Success in getting the leaderboard rankings with code: %s"), *LeaderboardObj->LeaderboardName.ToString());
#endif
// Return leaderboard information along with its members' user info.
TArray<ULeaderboardRank*> Rankings;
for (const FOnlineStatsRow& Row : LeaderboardObj->Rows)
{
if (!Row.PlayerId.IsValid())
{
continue;
}
// Get the member's display name.
const TSharedPtr<FOnlineUser> LeaderboardMember =
UserInterface->GetUserInfo(LocalUserNum, Row.PlayerId->AsShared().Get());
const FString DisplayName = !LeaderboardMember->GetDisplayName().IsEmpty() ?
LeaderboardMember->GetDisplayName() :
FText::Format(DEFAULT_LEADERBOARD_DISPLAY_NAME, FText::FromString(Row.NickName.Left(5))).ToString();
// Get the member's stat value.
float Score = 0.0f;
if (Row.Columns.Contains(TEXT("AllTime_Point")))
{
// The stat key is "AllTime_Point" if it was retrieved from FOnlineLeaderboardAccelByte::ReadLeaderboardsAroundRank().
Row.Columns[TEXT("AllTime_Point")].GetValue(Score);
}
else if (Row.Columns.Contains(TEXT("Point")))
{
// The stat key is "Point" if it was retrieved from FOnlineLeaderboardAccelByte::ReadLeaderboards()
Row.Columns[TEXT("Point")].GetValue(Score);
}
// Add a new ranking object.
ULeaderboardRank* NewRanking = NewObject<ULeaderboardRank>();
NewRanking->Init(Row.PlayerId, Row.Rank, DisplayName, Score);
Rankings.Add(NewRanking);
}
OnComplete.ExecuteIfBound(true, Rankings);
}
プレイヤーのリーダーボードランキングの取得
特定の範囲内のリーダーボードランキングを取得する機能を実装しました。このセクションでは、ローカルプレイヤーのリーダーボードランクを取得する機能を実装します。これにより、プレイヤーが特定のリーダーボードランキング範囲に含まれていない場合に、後で表示できるようになります。
-
LeaderboardSubsystem_Starterクラスのヘッダーファイルを開き、以下の関数を宣言します。public:
// ...
void GetPlayerRanking(const APlayerController* PC, const FString& LeaderboardCode, const FOnGetLeaderboardRankingComplete& OnComplete = FOnGetLeaderboardRankingComplete()); -
LeaderboardSubsystem_Starterクラスの CPP ファイルを開き、上記の関数を定義します。この関数は、特定のプレイヤーのリーダーボードランクを取得するリクエストを送信します。コールバックは、OnGetRankingsComplete()関数でも処理されます。void ULeaderboardSubsystem_Starter::GetPlayerRanking(const APlayerController* PC, const FString& LeaderboardCode, const FOnGetLeaderboardRankingComplete& OnComplete)
{
if (!ensure(LeaderboardInterface.IsValid()) || !ensure(UserInterface.IsValid()))
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Cannot get player leaderboard ranking. Leaderboard Interface or User Interface is not valid."));
return;
}
if (!ensure(PC))
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Cannot get player leaderboard ranking. PlayerController is null."));
return;
}
const FUniqueNetIdPtr PlayerNetId = GetUniqueNetIdFromPlayerController(PC);
if (!ensure(PlayerNetId.IsValid()))
{
UE_LOG_LEADERBOARD_ESSENTIALS(Warning, TEXT("Cannot get player leaderboard ranking. Player's UniqueNetId is not valid."));
return;
}
const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
FOnlineLeaderboardReadRef LeaderboardObj = MakeShared<FOnlineLeaderboardRead, ESPMode::ThreadSafe>();
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
LeaderboardObj->LeaderboardName = LeaderboardCode;
#else
LeaderboardObj->LeaderboardName = FName(LeaderboardCode);
#endif
// Get the player's leaderboard ranking.
OnLeaderboardReadCompleteDelegateHandle = LeaderboardInterface->AddOnLeaderboardReadCompleteDelegate_Handle(FOnLeaderboardReadCompleteDelegate::CreateUObject(this, &ThisClass::OnGetRankingsComplete, LocalUserNum, LeaderboardObj, OnComplete));
LeaderboardInterface->ReadLeaderboards(TPartyMemberArray{ PlayerNetId->AsShared() }, LeaderboardObj);
}
リソース
-
このチュートリアルセクションで使用されるファイルは、Unreal Byte Wars GitHub リポジトリで入手できます。
- AccelByteWars/Source/AccelByteWars/TutorialModules/Engagement/LeaderboardEssentials/LeaderboardSubsystem_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Engagement/LeaderboardEssentials/LeaderboardSubsystem_Starter.cpp
- AccelByteWars/Source/AccelByteWars/TutorialModules/Engagement/LeaderboardEssentials/LeaderboardEssentialsModels.h