> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Leaderboards

> イベントベースの stats と Partner Center stat ルールを使用して XBOX GDK で Steam リーダーボードを再現し、2 つのリーダーボード API を比較するコード サンプルを示します。

XBOX services のリーダーボードは、ユーザー統計から派生し、クエリによって構築されます。これは、API にアップロードされる「スコア」で駆動される Steam のリーダーボードとは異なります。実績と同様、XBOX Game Development Kit (GDK) には、event-based と title-managed の 2 つの統計 API があります。実績とは異なり、title-managed よりも event-based の統計とリーダーボードを使用することを推奨します。このトピックではその API に焦点を当てます。この 2 つの API の違いの詳細については、[Event-based と title-managed Stats](/services/xbox-services/player-data/stats-leaderboards/index) を参照してください。

両方のプラットフォームは、それらのポータルでリーダーボードを定義します。違いは、XBOX services では最初に Partner Center で stat ルールを定義し、そのルールが駆動する統計に基づいてリーダーボードを構築することです。一方、Steam ではこの 2 つを区別しません: ポータルまたはプログラム的にリーダーボードを作成し、そのリーダーボードに直接ユーザー スコアをアップロードします。

Steam Leaderboard API と比較して、これらの各 API がどのように使用できるかを説明するため、Steam SDK と event-based XBOX Game Development Kit (GDK) の stats プラットフォーム上で、最長走破距離用のリーダーボードを作成する方法（この例は Steamworks API ドキュメントから）についてのコード例を提供します。以下のセクションはロジックを説明します。

## ポータルで stat / リーダーボードを作成する

### Steamworks

Steamworks 管理ポータルで、**Stats & Achievements** > **Leaderboards** に移動します。次のスクリーンショット（Steamworks ドキュメントから）に示すようにフォームを入力します。

<img src="https://mintcdn.com/microsoft-4404708b/5IpvKlT-jmAaVkeY/images/nda/steam-porting-guide/steam-portal-add-leaderboard.png?fit=max&auto=format&n=5IpvKlT-jmAaVkeY&q=85&s=e8a3f4a76f8a9bdc60689571bf5a9dbb" alt="Screenshot of the form used to create a leaderboard in the Steamworks Admin Portal" width="806" height="190" data-path="images/nda/steam-porting-guide/steam-portal-add-leaderboard.png" />

### XBOX Game Development Kit (GDK)（event-based）

Partner Center でゲームに移動し、**XBOX services** > **Gameplay settings** を選択します。右ペインのナビゲーション バーで、**Player Stats** > **Stat Rules** を選択し、stat の要件に合わせて構成した **Show** として stat ルールを追加します。stat の更新には、プレイヤー（ユーザー）が前回の更新以来どれだけ移動したかの計算値が含まれるため、サービスがこの値を取得する測定フィールドを指定します。

<Note>
  イベントを更新するデータを、イベントの Measurements または Dimensions フィールドに保存しても機能的な違いはありません。2 つのフィールドは Application Insights で使用するために存在します。この詳細については、\[Application Insights API for custom events and
</Note>

metrics]\([https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-custom-events-metrics](https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-custom-events-metrics)) を参照してください。

次のスクリーンショットは、Partner Center で stat ルールを作成するのに使用されるフォームを示しています。

<img src="https://mintcdn.com/microsoft-4404708b/5IpvKlT-jmAaVkeY/images/nda/steam-porting-guide/partner-center-stat-rule.png?fit=max&auto=format&n=5IpvKlT-jmAaVkeY&q=85&s=08b05f51f44f9f912854674ea3c189ae" alt="Screenshot of the form used to create a stat rule in Partner Center" width="1196" height="1046" data-path="images/nda/steam-porting-guide/partner-center-stat-rule.png" />

保存した後、右ペインの上部バーで **Leaderboard** を選択し、以下のスクリーンショットに示すように、作成した stat が駆動する新しいリーダーボードを作成します。

<img src="https://mintcdn.com/microsoft-4404708b/5IpvKlT-jmAaVkeY/images/nda/steam-porting-guide/partner-center-leaderboard.png?fit=max&auto=format&n=5IpvKlT-jmAaVkeY&q=85&s=5ffd10e8fdb53ed640fcb59ef31e323a" alt="Screenshot of the form used to create a leaderboard in Partner Center" width="1048" height="654" data-path="images/nda/steam-porting-guide/partner-center-leaderboard.png" />

## スコアをアップロードする

### Steamworks

Steamworks では、移動したフィートの新しい合計値を計算し、`ISteamUserStats::UploaderLeaderboardScore` メソッドを使用して API に送信する必要があります。

```cpp theme={null}
int32 newFeetTraveled; // 好きなように計算する。
const int32* scoreDetails = {}; // スコアに関連する追加値。
SteamLeaderboard_t leaderboard; // スコアをアップロードするリーダーボードのハンドル...
SteamAPICall_t result = UploadLeaderboardScore( leaderboard, ISteamUserStats::k_ELeaderboardUploadScoreMethodForceUpdate, 100, scoreDetails, int 0);
STEAM_CALLBACK(MyGameClass, OnLeaderboardScoreUploaded, LeaderboardScoreUploaded_t);
// OnLeaderboardScoreUploaded コールバック関数でエラーを処理する。
```

### XBOX Game Development Kit (GDK)（event-based）

XBOX Game Development Kit (GDK) のイベントベース stats では、イベント受信時にこの stat をインクリメントすることを指定し、この `diff` 値を measurements JSON 文字列のどこに見つけるかを指定しているため、移動したフィートの差分を [`XblEventsWriteInGameEvent`](/reference/live/xsapi-c/events_c/events_c_members) に渡すだけです。以下に示します。

```cpp theme={null}
// これはユーザーが前回の更新以来移動したフィートを保持していると仮定する。
int diffFeetTraveled; 
std::stringstream ss;
ss << "{ \"feet\": " << diffFeetTraveled << " }";
std::string measurements = ss.str();
HRESULT hr = XblEventsWriteInGameEvent(
    m_xboxLiveContext,
    "FeetTraveled",
    "",
    measurements.c_str()
);
```

詳細については、[Writing an event to power an event-based Stat](/services/xbox-services/player-data/stats-leaderboards/index) を参照してください。

## グローバル リーダーボードを取得する

Steamworks SDK では、`ISteamUserStats::GetLeaderboardEntries` を呼び出し、`callback` 関数内で `ISteamUserStats::GetDownloadedLeaderboardEntry` を呼び出してリーダーボードの各エントリ、つまり「行」を取得することで、グローバル リーダーボードを取得できます。

### Steamworks

```cpp theme={null}
class MyGameClass
{
    void OnFindLeaderboardCompleted(LeaderboardFindResult_t *callback);
    SteamAPICall_t m_getLoaderboardCall;
    SteamAPICall_t m_getEntriesCall;
};

m_getLoaderboardCall = SteamUserStats()->FindLeaderboard(leaderboardName);
STEAM_CALLBACK(MyGameClass, OnFindLeaderboardCompleted, LeaderboardFindResult_t);

// ...

void MyGameClass::OnFindLeaderboardCompleted(LeaderboardFindResult_t *callback)
{
    if (callback->bLeaderboardFound)
    {
        auto handle = callback->m_hSteamLeaderboard;
        const char *leaderboardName = SteamUserStats()->GetLeaderboardName(handle);
        m_getEntriesCall = SteamUserStats()->GetLeaderboardEntries(handle, ELeaderboardDataRequest::k_ELeaderboardDataRequestGlobal, 0, 100);
        STEAM_CALLBACK(MyGameClass, OnLeaderboardScoresDownloaded, LeaderboardScoresDownloaded_t);
    }
    else
    {
        // リーダーボードが見つからなかった。
    }

void MyGameClass::OnLeaderboardScoresDownloaded(LeaderboardScoresDownloaded_t *callback)
{
    int numScores = callback->m_cEntryCount;
    for (int i = 0; i < numScores; i++)
    {
        LeaderboardEntry_t entry;
        bool result = SteamUserStats()->GetDownloadedLeaderboardEntry(callback->m_hSteamLeaderboardEntries, i, &entry, nullptr, 0);
        if (!result)
        {
            // エラーを処理する。
        }
        CSteamID userId = entry.m_steamIDUser;
        int32 rank = entry.m_nglobalRank;
        int32 score = entry.m_nScore;
        // データで何かを行う。 
    }
}
```

### XBOX Game Development Kit (GDK)（event-based）

XBOX Game Development Kit (GDK) API は類似のパターンに従います。どのリーダーボードが必要か、どのスコープをカバーするか、どう並べ替えるか、含める追加フィールド（ある場合）などを指定するクエリを構築します。クエリで [`XblLeaderboardGetLeaderboardAsync`](/reference/live/xsapi-c/leaderboard_c/leaderboard_c_members) を呼び出した後、[`XblLeaderboardGetLeaderboardResultSize`](/reference/live/xsapi-c/leaderboard_c/leaderboard_c_members) と [`XblLeaderboardGetLeaderboardResult`](/reference/live/xsapi-c/leaderboard_c/leaderboard_c_members) を使用して、指定されたバッファに結果を入れ、次のコード例に示すようにそれらを反復処理できます。

```cpp theme={null}
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = m_taskQueue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // XAsyncBlock* の所有権を引き継ぐ
    size_t resultSize;
    std::vector<uint8_t> leaderboardBuffer;
    HRESULT hr = XblLeaderboardGetLeaderboardResultSize(asyncBlock, &resultSize);

    if (SUCCEEDED(hr))
    {
        leaderboardBuffer.resize(resultSize);
        XblLeaderboardResult* leaderboard{};

        hr = XblLeaderboardGetLeaderboardResult(asyncBlock, resultSize, leaderboardBuffer.data(), &leaderboard, nullptr);

        if (SUCCEEDED(hr))
        {
            // result 内の XblLeaderboardResult を使用する。
            for (int row = 0; row < leaderboard->rowsCount; row++)
            {
                uint64_t xuid = leaderboard->rows[row].xboxUserId;
                uint32_t rank = leaderboard->rows[row].rank;
                const char** values = leaderboard->rows[row].columnValues;
                // データで何かを行う。
            }
        }
    }
};

XblLeaderboardQuery leaderboardQuery = {}; 
strcpy(&leaderboardQuery.scid[0], m_scid.c_str());
leaderboardQuery.leaderboardName = leaderboardName.c_str(); 
leaderboardQuery.xboxUserId = 0;
leaderboardQuery.order = XblLeaderboardSortOrder::Descending;
leaderboardQuery.skipResultToRank = 0;
leaderboardQuery.maxItems = 100;
leaderboardQuery.statName = "MyStatName";
leaderboardQuery.socialGroup = XblLeaderboardQueryType::None;
// XblLeaderboardQuery の詳細なオプションについては、次のリンクを参照してください。

HRESULT hr = XblLeaderboardGetLeaderboardAsync(
    xboxLiveContext,
    leaderboardQuery,
    asyncBlock.get());
if (SUCCEEDED(hr))
{
    // 呼び出しが成功したので、コールバックが所有権を引き継ぐため、XAsyncBlock* の std::unique_ptr の所有権を解放する。
    // 呼び出しが失敗した場合、std::unique_ptr は所有権を保持し、XAsyncBlock* を削除する。
    asyncBlock.release();
}
```

詳細については、以下を参照してください。

* [Example code for event-based Leaderboards](/services/xbox-services/player-data/stats-leaderboards/index)
* [XblLeaderboardQuery](/reference/live/xsapi-c/leaderboard_c/leaderboard_c_members)

## ソーシャル リーダーボードを取得する

Steamworks では、`ELeaderboardDataRequest::k_ELeaderboardDataRequestFriends` 列挙型を使用してリーダーボードを現在のユーザーのフレンドにスコープできます。

XBOX Game Development Kit (GDK) では、これは *ソーシャル リーダーボード* として知られています。XBOX services からソーシャル リーダーボードを取得するには、[`XblLeaderboardQuery`](/reference/live/xsapi-c/leaderboard_c/leaderboard_c_members) 構造体で `xboxIUserId` を XBOX services にサインインしている現在のユーザーの XBOX User ID (XUID) に、`leaderboardName` を `nullptr` に、`socialGroup` を `XblSocialGroupType::People` または `XblSocialGroupType::Favorites` に設定します。前述のように `XblLeaderboardGetLeaderboardAsync` を呼び出します。

詳細については、[XblSocialGroupType](/reference/live/xsapi-c/leaderboard_c/leaderboard_c_members) を参照してください。

## Featured Stats

Steam のようなリーダーボードを生成するために使用される通常のユーザー統計に加えて、XBOX services には *Featured Stats* と呼ばれる追加のコンセプトがあります。Featured Stats は、ゲームのために XBOX エコシステム全体のいくつかの異なるサーフェスに表示され、ユーザーの統計の値や既に生成されているリーダーボードを表示できます。ゲームには最大 20 個の Featured Stats を作成できます。

Featured Stats とその追加方法の詳細については、[Featured Stats overview](/services/xbox-services/player-data/stats-leaderboards/index) と [Portal configuration of event-based Featured Stats](/services/xbox-services/player-data/stats-leaderboards/index) を参照してください。

## レート制限

イベントを書き込むとき、XBOX Services API (XSAPI) をあまりに頻繁に呼び出さないように注意してください。呼び出しすぎると、ゲームがレート制限に達する可能性があります。これは、ユーザーにとって予期しない挙動と悪い体験を引き起こす可能性があります。これは Steam では問題ではない XBOX services 固有の懸念です。Steam に stat 値を書き込むことは、ゲームが明示的にそれらを同期するまでサーバーへの API 呼び出しを引き起こしません。

頻繁に発生するイベントについては、ある数のイベントが発生した後に、または一定の時間間隔でサーバー上で更新するように、API 呼び出しをバッチ処理してみてください。

[XBOX Live Trace Analyzer (XblTraceAnalyzer.exe)](/tools/tools-services/live-trace-analyzer) を使って API 呼び出しをデバッグできます。

XBOX services のレート制限の詳細については、[Fine-Grained Rate Limiting](/services/xbox-services/develop/best-practices/live-fine-grained-rate-limiting) を参照してください。


## Related topics

- [PFLeaderboardsLeaderboardColumn](/ja-jp/services/playfab/api-references/c/pfleaderboardstypes/structs/pfleaderboardsleaderboardcolumn.md)
- [PFLeaderboardsLeaderboardDefinition](/ja-jp/services/playfab/api-references/c/pfleaderboardstypes/structs/pfleaderboardsleaderboarddefinition.md)
- [PFLeaderboardsLeaderboardEntryUpdate](/ja-jp/services/playfab/api-references/c/pfleaderboardstypes/structs/pfleaderboardsleaderboardentryupdate.md)
- [PFLeaderboardsLeaderboardSortDirection](/ja-jp/services/playfab/api-references/c/pfleaderboardstypes/enums/pfleaderboardsleaderboardsortdirection.md)
- [PFLeaderboardsEntityLeaderboardEntry](/ja-jp/services/playfab/api-references/c/pfleaderboardstypes/structs/pfleaderboardsentityleaderboardentry.md)
