メインコンテンツまでスキップ

OSS を使用してリーダーボードを表示する - 全期間のリーダーボード - (Unreal Engine モジュール)

Last updated on February 4, 2026

注釈:本資料は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 クラスには、いくつかの機能が提供されています。

  • LeaderboardInterfaceUserInterface という名前の 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*/);

リーダーボードランキングの取得

このセクションでは、リーダーボードランキングを取得する機能を実装します。

  1. LeaderboardSubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。

    public:
    // ...
    void GetRankings(const APlayerController* PC, const FString& LeaderboardCode, const int32 ResultLimit, const FOnGetLeaderboardRankingComplete& OnComplete = FOnGetLeaderboardRankingComplete());
  2. リーダーボードランキングの取得プロセスが完了したときに処理するコールバック関数を宣言します。

    protected:
    // ...
    void OnGetRankingsComplete(bool bWasSuccessful, const int32 LocalUserNum, const FOnlineLeaderboardReadRef LeaderboardObj, const FOnGetLeaderboardRankingComplete OnComplete);
  3. リーダーボードリストからユーザー情報をクエリするコールバック関数を宣言します。

    protected:
    // ...
    void OnQueryUserInfoComplete(
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo,
    const int32 LocalUserNum,
    const FOnlineLeaderboardReadRef LeaderboardObj,
    const FOnGetLeaderboardRankingComplete OnComplete);
  4. 上記の関数を定義します。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);
    }
  5. 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);
    }

プレイヤーのリーダーボードランキングの取得

特定の範囲内のリーダーボードランキングを取得する機能を実装しました。このセクションでは、ローカルプレイヤーのリーダーボードランクを取得する機能を実装します。これにより、プレイヤーが特定のリーダーボードランキング範囲に含まれていない場合に、後で表示できるようになります。

  1. LeaderboardSubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。

    public:
    // ...
    void GetPlayerRanking(const APlayerController* PC, const FString& LeaderboardCode, const FOnGetLeaderboardRankingComplete& OnComplete = FOnGetLeaderboardRankingComplete());
  2. 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);
    }

リソース