Unreal Engine4+PlayFab　かんたんにオンラインランキングを作る～実装編：基本～

# 前置き
前回の<a href="https://qiita.com/Dish_HC/items/38682d30a48e80eaa7e3">準備編</a>を踏まえての内容です。

それではオンラインランキングの実装を進めていきます。
・PlayFab管理画面(Web)からLeaderboardの設定
・プロジェクト内(UE4)での実装

Jsonファイルを使用することになりますので、Jsonファイルの書式を理解しておいてもらえるとスムーズに理解ができると思います。

ここではかんたんに実装していきますので、チート対策としては非常に良くないであろう実装が含まれていますがご了承ください。

# PlayFab管理画面からLeaderboardの設定
左側メニューのLeaderboardを選択し、新しいLeaderboardを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/5301d91e-3967-9acd-1616-2e95dd7aa7ea.png)
Statistic Nameは何でもいいのでここではTestGameとします。
AggregationMethodはここでは一旦Lastを選択してSaveします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/b925842a-d88d-a587-5eda-f820965e8d12.png)
このAggregationMethodで保存するデータの制御が可能です。
　・Last：最大、最小問わず最後に送信されたデータを保存
　・Minimum：最小値を保存、最小値よりも大きい値を送信しても保存されない(タイムアタックなど)
　・Maximum：最大値を保存、最大値よりも小さい値を送信しても保存されない(スコアアタックなど)
　・Sum：送信したデータを足し算していく(クリア回数ランキングなど)

次に、左上の歯車マークからTitleSettingsを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/72dd1f18-4b2e-47ba-fb36-880527f997f5.png)
上部のAPI Featuresを選択した上で、中段辺りにある"Allow client to tpost player statistics"にチェックを入れてSaveします。
これはクライアントからサーバーにデータを送信できるようにするためのもので、ここをオンにしていないと送信ができません。

<font color="FF0000">※2022/2/28修正
改めて管理画面を開いたところ、Cloudscriptの使用ができそうでした。
以前はAutmation欄にRevisionという項目が表示されておらず使用ができませんでしたが、原因不明ながら表示されるようになっていました。
登録してから有効になるまで時間がかかるのか何なのかは不明ですが、使えるとのことなので実装テストをしてみて実行できそうであれば別途記事にしたいと思います。</font>

<font color="FF0000">※本来は、チートができないようデータの書き込みはサーバー側で処理すべきです
が、残念ながら2022/2月現在、新規登録した場合にはAzureFunctionに統合されたようでCloudScriptが使用できないようになっています。
ネット記事にあるものはほぼ大半がCloudScriptを使用したものになっていますので参考にならず、私の知識では自前でAzureFunctionでの実装を実現することはできませんでした。
この辺り詳しい方いらっしゃいましたら参考記事など是非教えて欲しいです…。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/6779d926-6488-22f5-597b-811be2c3c667.png)</font>


# プロジェクト内での実装

前回はまずPlayFabにログインするところまでを作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/4bf08cff-38fb-2fe9-3c12-5831ba4907e4.png)
これからLeaderboardへの送信、Leaderboardの読み込み部分を作成していきます。
作成にあたって、まずこのログイン部分を使って少々解説をしておきます。

# 書式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/26ca3e2a-bbf4-cd1f-0537-af2d126b68a7.png)
この後もこういった、Request,OnSuccess,OnFailureのインプットピンがついたノードがいくつか出てきます。
全て繋がないと動作しませんので覚えておいてください。

PlayFabのサーバーへ何かしらの要求を行う際、Requestという構造体を使用する必要があります。
RequestピンからドラッグすることでMakeで適したものを作成ができますのでここから作成しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/f5224c44-a1bd-c153-6b8f-ebb6c866da0e.png)
また、この後の実装について、UEのアウトプットログを使用すると理解しやすくなりますので、開きながら作業するのをオススメしておきます。
ウィンドウ→デベロッパーツール→アウトプットログで表示ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/06d34d8e-b369-022d-7de4-d22716b17374.png)
# 前回のログイン部分の補足
CustomIDでのログインはPlayFabでは推奨されていませんが、テストを行うには非常にかんたんにログインができますので今回はこちらを使用します。
※公式のベストプラクティスを見ると、Googleやその他サービスのアカウントでのメールアドレスとパスワードを用いたログインが推奨されています。

https://docs.microsoft.com/ja-jp/gaming/playfab/features/authentication/login/login-basics-best-practices

Custom IDピンに入力したIDでログインを試みますが、初回ログインの場合はPlayFab上にプレイヤーのアカウントが存在しません。
Create Accountのboolにチェックを入れておくことで、存在しないCustomIDである場合には新たにプレイヤーを作成してくれますのでチェックを入れています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/ef93c206-4fbb-a846-b546-2c4775ceb1ed.png)

# スコアを送信してみよう
まず全体のブループリントです。
やっている事自体はかんたんで、JsonファイルをRequestに含んで送信を行っているだけです。
初心者の私はJsonファイルがどういう書式かすらわかっていませんでしたので戸惑いましたが…。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/3414f8fd-6135-e6cc-13bc-819761bc692a.png)

さて、順番に説明します。
スコアを送信するためには、UpdatePlayerStatisticsノードを使用します。
Requestにスコアデータが入ったJsonファイルを含んであげる必要がありますので、Jsonファイルを作成しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/19bbd1bc-eee0-85a3-6eef-91617eae9564.png)

PlayFabプラグインにより、ブループリントでJsonファイルの作成が可能になっています。
Construct Json ObjectノードによりJsonファイルを作成、変数として保存します。
次に以下のように組みます。
　Set String Fieldノード
　　FieldNameに"StatisticName"、String Valueに"TestGame"
　　　(PlayFabの管理画面で設定したLeaderboardの名前をString Valueに入力)
　Set Fieldノード
　　FieldNameに"Value"、JsonValueにはConstruct Json Number Valueノードを使用してFloat型の変数を変換してセットします。
※ここでは分かりやすく、ConstructJsonNumberValueに直接5000を入力しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/a21672d0-059d-1d67-6975-f26b832770fa.png)
これで作成したJsonファイルは以下のような状態になっています。
この処理を実行した際、アウトプットログでこの内容が確認できます。
送信した際のデータがおかしい場合など、アウトプットログを確認することで何がおかしいのか、デバッグの際はアウトプットログを注視しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/57ed63ae-dd83-767e-a227-064c2e24e504.png)
Jsonファイルが出来たので、これをUpdatePlayerStatisticsノードを使用してLeaderboardに送信しましょう。
UpdatePlayerStatisticsノードのRequestピンをドラッグしてMake ClientUpdatePlayerStatisticsRequestノードを呼び出します。
インプットピンのStatisticsが配列でないと入力できませんので、先程保存しておいたJsonファイル変数からMakeArrayを使用して配列化してインプットピンに繋ぎます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/947231cc-8e59-9fb9-fac7-ea35473bed30.png)
UpdatePlayerStatisticsノードにはOnSuccessとOnFailureがありますのでピンからドラッグしてカスタムイベントをそれぞれ作成した上で成功時、失敗時の処理を組みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/346bf453-ece7-c23e-bcb5-058d143161f9.png)
これで最初の画像の通り組み終わりました。
スペースキーで実行してみることにします。
成功時にはPrintStringでUpdateと表示するように組んでいますので実行すると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/471ee2bb-0b8c-5064-c169-1a2f3a224221.png)
無事Updateが表示されました！
それではPlayFabの管理画面に移動してLeaderboardにデータ保存されているか確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/a487af80-0af6-041f-7003-4f60bc99d7d9.png)
管理画面の方でもLeaderboard上に送信したスコアが反映されています！
これでスコア送信ができるようになりました。
あとはゲーム内でこのLeaderboardの読み込みができればランキングが完成したも同然ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/0373f7ae-12e8-a154-849c-ed5d1cc29fcb.png)
# Leaderboardデータの読み込み
こちらもまず全体のブループリントから。
非常にかんたんで、GetLeaderboardノードで取得することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/1faa2ffd-79ac-5365-8f40-c52008555096.png)
まず、GetLeaderboardノードを設置、RequestピンからMake ClientGetLeaderboardRequestを作成します。
StatisticNameにはPlayFab管理画面で設定したLeaderboardの名前、"TestGame"を設定します。
Max Results Countは取得する数で、ここでは一旦100を設定。
StartPositionはトップから何番目をスタートとするか、なので0とします。
これでトップ1~100までが取得したい、というRequest内容になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/1eb1fb65-8627-2caa-3b0e-1a8cd0d523b4.png)
GetLeaderboardノードのリクエストが成功すると、Json配列でデータを取得できますのでReverseForEachLoopにて表示したいと思います。
OnSuccessからピンを伸ばしてカスタムイベントを作成するとインプットピンにResultがあると思いますので、ResultピンをドラッグしてBreakで構造体を分解します。
Leaderboard配列にデータが入っていますので、これをReverseForEachLoopに繋ぎます。
ArrayElementからピンを伸ばし、GetFieldノードのFieldNameに"Position"(順位)を指定してこれをAsStringでString型に変換します。
同じように、スコアも取得したいと思いますのでGetFieldノードのFieldNameに"StatValue"(スコア)を指定、これもAsStringでString型に変換。
一行で表示したいと思いますので、Appendノードを用いてつなげた上でPrintstringで表示します。
忘れずにOnFailureも繋いでおきましょう。
これで表示部分も完成しました。
ただ、ここまでの手順どおりにして頂いた方はこのままではLeaderboardには一行しかないので、1行しか表示されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/a337e788-4b51-f192-717d-d7129b687ed3.png)

ちゃんと動作するかチェックしたいので、プレイヤーとスコアを量産するためにテストとして以下のようにログインとスコア送信部分を作って大量に量産したいと思います。
※但し！PlayFabの管理画面からプレイヤーを削除するのが無茶苦茶手間なので、テストが終わったら自身のタイトル自体は削除してしまった方が楽です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/e4bcba61-c046-c7cb-5297-581235bee4a3.png)
とりあえずこれで126まで作成してみました。
さぁ読み込みです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/e5a20c57-53d5-1623-475a-773aca5957e3.png)
適当に実行用イベントを作って…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/fd4f89be-0d4b-3ff4-638d-47b8324f46b2.png)
はい！読み込みもできました！！！
あとはこれをUIなどに起こせばオンラインランキングの完成です！！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/a7511efa-0884-34e3-71f1-7517ef130dcd.png)
# あとがき
いかがでしたでしょうか？
ものすごく簡単にオンラインランキングが作れました。
基本の順位とスコアの取得のみを記事にしましたが、実際にはもっと色んなデータを取得できます。
そのうち応用編としてこちらも紹介しようかと思いますが、書くのに結構な時間がかかるのでいましばらくお待ちください。

分かりにくい箇所やわからない場合にはコメントでもいただければ修正しますのでお気軽にご意見ください。