Azure PlayFabのとても便利なランキング機能を使ってみた&ついでにランキング報酬も配ってみた

## まえがき
オンラインゲームにかかせないランキング機能、自分で作るのは少し面倒ですよね。
PlayFab にはユーザーのランキングを管理したり、ランキングに応じて報酬を配ったりできる機能があります。

試しに使ってみたところ、とても便利な機能だったので記事に残しておこうと思います。

## 成果物
※ゲームプレイ後に超簡易的なランキング画面が表示されます。
![JumpRope.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/cdf4e5d3-bd0f-46ed-7e7f-ece1d568cdac.gif)

## 使用する PlayFab の API
- [UpdateUserTitleDisplayName - ランキングなどに表示するユーザー名を登録します](https://api.playfab.com/documentation/client/method/UpdateUserTitleDisplayName)
- [UpdatePlayerStatistics - ランキングの集計対象にするデータ（スコアなど）を登録します](https://api.playfab.com/documentation/client/method/UpdatePlayerStatistics)
- [GetLeaderboard - ランキングを取得します（1位から10位のランキングを取得する場合などはこちら）](https://api.playfab.com/documentation/client/method/GetLeaderboard)
- [GetLeaderboardAroundPlayer - ランキングを取得します（自分の前後のランキングを取得する場合などはこちら）](https://api.playfab.com/documentation/Client/method/GetLeaderboardAroundPlayer)

## 1. 事前準備
### 1.1. クライアントから PlayFab への統計情報のポストを許可しておく
PlayFab は統計情報（スコアなど）をクライアントから送信することをデフォルトでは禁止しています。

チート対策のために基本的にはサーバー側で処理してね、とのことですが今回は個人制作の趣味のゲームですし、クライアントからサクッと値を送信したいと思います。

そのために GameManager の設定画面で `クライアントにプレイヤー統計情報のポストを許可する` を有効にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/320b4016-cdb7-6d11-1cc9-2d56a4fea2ed.png)

これを忘れるとクライアントから統計情報を送信したときに下記のエラーがでるので注意してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/0b618f82-db73-1eaa-5592-93b1b3a55c03.png)

### 1.2. ランキングの定義を作成しておく
GameManager で新しいランキングを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/2eab891b-2704-0190-0731-c3b0dc5a3887.png)

続いてどのようなランキングを作成するか決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/e28c1d9d-77f0-7b89-3e60-7ed7b962865d.png)

- 統計情報名
わかりやすい名前ならなんでも大丈夫です。
一度決めると変更できないので慎重に。

- リセット頻度
今回は手動にしますが、毎時/毎日/毎週/毎月なども選べます。
ランキングリセットの際は自動で報酬を配布したりすることもできます。（後述）

- 集計方法
今回はプレイヤーごとの最多ジャンプ成功数をランキング表示したいので `最大` にしておきます。


### 1.3. ユーザーの DisplayName を登録しておく
ランキングにユーザー名を表示するためにはユーザーの DisplayName を登録しておく必要があります。
ゲーム内でユーザー名を設定する画面を作り、以下のようなコードで PlayFab へ登録しておきましょう。

```C#
    private void SetUserName(string userName)
    {
        var request = new UpdateUserTitleDisplayNameRequest
        {
            DisplayName = userName
        };

        PlayFabClientAPI.UpdateUserTitleDisplayName(request, OnSuccess, OnError);

        void OnSuccess(UpdateUserTitleDisplayNameResult result)
        {
            Debug.Log("success!");
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }
```

ちなみに登録した DisplayName は GameManager のプレイヤー一覧などにも自動的に表示されます。
地味に嬉しいですね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/4007e2fe-bb4f-6488-7f59-fcac855fd835.png)

## 2. ランキングの実装
### 2.1. 統計情報（スコア）の送信
以下のようなコードで統計情報を送信することができます。

```C#
    private void SendPlayScore(int score)
    {
        var statisticUpdate = new StatisticUpdate
        {
            // 統計情報名を指定します。
            StatisticName = "JumpCount",
            Value = score,
        };

        var request = new UpdatePlayerStatisticsRequest
        {
            Statistics = new List<StatisticUpdate>
            {
                statisticUpdate
            }
        };

        PlayFabClientAPI.UpdatePlayerStatistics(request, OnSuccess, OnError);

        void OnSuccess(UpdatePlayerStatisticsResult result)
        {
            Debug.Log("success!");
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }
```
※送信した統計情報がランキングに反映されるまで1～2秒かかる場合があります。
※ですので送信後すぐにランキングを取得するような設計は控えましょう。
※[こちらの記事](https://qiita.com/zurachu/items/569301f033052966c4fa)で詳しく解説されています。

### 2.2. ランキングの取得
以下のようなコードでランキングを取得することができます。

```C#
    private void GetRanking()
    {
        var request = new GetLeaderboardRequest
        {
            StatisticName = "JumpCount", // 統計情報名を指定します。
            StartPosition = 0, // 何位以降のランキングを取得するか指定します。
            MaxResultsCount = 100 // ランキングデータを何件取得するか指定します。最大が100です。
        };

        PlayFabClientAPI.GetLeaderboard(request, OnSuccess, OnError);

        void OnSuccess(GetLeaderboardResult leaderboardResult)
        {
            // 実際は良い感じのランキングを表示するコードにします。
            foreach (var item in leaderboardResult.Leaderboard)
            {
                // Positionは順位です。0から始まるので+1して表示しています。
                Debug.Log($"{item.Position + 1}位: {item.DisplayName} - {item.StatValue}回");
            }
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }
```

実行すると以下のような結果が得られます。
これを良い感じに UI に組み込むだけでランキングの完成です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/fba5298c-11f5-d07d-88dc-a168a401cfd9.png)

ちなみにランキングのデータは GameManager で確認することが可能です。便利ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/c0c782d7-e6c6-86e0-b789-aaad57a47482.png)

そういえば今回は1位からのランキングを取得/表示しましたが、実際は自分の+-5位のランキングを表示したかったりすると思います。

その場合は GetLeaderboard の代わりに [GetLeaderboardAroundPlayer](https://api.playfab.com/documentation/Client/method/GetLeaderboardAroundPlayer) を使います。
コードは殆ど同じです。

```C#
    private void GetRanking()
    {
        var request = new GetLeaderboardAroundPlayerRequest
        {
            StatisticName = "JumpCount", // 統計情報名を指定します。
            MaxResultsCount = 11 // 自分と+-5位をあわせて合計11件を取得します。
        };

        PlayFabClientAPI.GetLeaderboardAroundPlayer(request, OnSuccess, OnError);

        void OnSuccess(GetLeaderboardAroundPlayerResult leaderboardResult)
        {
            // 実際は良い感じのランキングを表示するコードにします。
            foreach (var item in leaderboardResult.Leaderboard)
            {
                // Positionは順位です。0から始まるので+1して表示しています。
                Debug.Log($"{item.Position + 1}位: {item.DisplayName} - {item.StatValue}回");
            }
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }
```

ゲーム内の実装の話はここまでです。
最後にランキング報酬を配ってみましょう。

## 3. ランキング報酬の配布
GameManager でランキングを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/0631c4ba-6ecc-b9c3-7270-7a8b609ba749.png)

プライズテーブルを開いて、新しいプライズテーブルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/38d38c27-079a-8eb6-0564-7a1261690436.png)

ランキング1～5位には魔法石10個、6～10位には魔法石5個を配る設定を作ってみました。
画面が直感的に操作できて素敵です。（スクショが長くてごめんなさい）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/a9cb1e3d-ada6-9979-cc18-299dd8e19a16.png)
※アイテムは複数種類を一度に配布することも可能です。
※無料プランではランキング10位までしか報酬を配れないので注意してください。
※$99.99課金すると1000位まで配れるようになります。
※報酬を配る以外にも、ランキングに応じてメールを送信する、プッシュ通知を送る、自分で書いたスクリプトを実行するなどの処理が可能です。

JumpCount のランキング表示に戻って、今すぐリセットを押します。
イベントが終わったらポチッとリセットする感じの運用ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/42c5a445-b82b-3d95-037b-4233e0dc9e74.png)

確認画面が表示されるのでリセットしてください。
これでランキングがリセットされて、同時に報酬が配布されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/5a3432a2-b26f-5a51-c153-b79d29d725e1.png)

先程1位だったNishiさんのインベントリを覗いてみると、ちゃんと魔法石が配布されていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97193/f57a9a2b-a488-a47b-f755-86f13d552657.png)

## あとがき
ゲームにランキングを簡単に実装できると聞いて試してみましたが、想像以上に簡単で驚きました。
報酬の配布もとても便利で最高ではないでしょうか。