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

# 前置き
前々回、前回の内容を踏まえて、応用編としてPlayfabサーバーからもう少し詳細な情報を取得して表示します。

# Profile情報へのアクセス
アウトプットログを見てもらえばわかりますが、Leaderboadを取得した際の配列には各プレイヤーのProfileと呼ばれるデータも含まれています。

```ruby:Outputlog　※本来改行されてないが読み辛いため改行整形済み
LogPlayFab: Response : {
"code":200,"status":"OK",
"data":{"Leaderboard":[
　{"PlayFabId":"0000000000",
　　"StatValue":495773,
　　"Position":0,
　　"Profile":
　　　{"PublisherId":"0000000000000000",
　　　　"TitleId":"5B531",
　　　　"PlayerId":"220564CE0999C1B2"}},
~~~
```

上記の情報だけでは大したものがありませんが、読み込める情報を許可してあげればその他の情報も読み取りが可能となります。

2つの手順が必要となりますので解説します。
・PlayFab上のClientProfileOptionという箇所でアクセスを許可する情報を追加
・プロジェクト内でBlueprintによる取得情報の追加

# PlayFab上での許可
左上のゲーム名の隣にある歯車をクリック、その中のTitle Settingsを開いてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/11f07f9d-77a9-eefd-0a4b-c15c03946ae2.png)

Title Settings内の上部タブにClient Profile Optionsを開くと以下のような画面になります。
チェックが入った情報を読み込むことが可能になります。
今回はDisplayName(プレイヤー名)、Statistics(統計情報)、Location(国)にチェックを入れてSave client profile optionsを必ず押して保存します。
PlayFab上での操作は以上です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/37694f64-d283-6145-f458-d83649814c28.png)

# PlayFabへ情報の送信
さて、これでPlayFabから取得できるデータの制限は解除されました。
しかしながら、このままではそもそもサーバー上にあるプレイヤーのデータに名前も何もありませんので取得したところで情報が空です。
PlayFab上のユーザーデータにプレイヤー名を設定してみましょう。

Blueprintの全体像です。
Sキーを押すことでPlayFabへPlayerNameを送信する形になっています。
送信が失敗した際のFailureUpdateイベントの処理は組まなくてOK。
エラーコードが色々ありますが、PlayFabの公式ドキュメント上を参照してください。
仕様上、以下の2点は注意しておいた方がいいかと。
・プレイヤー名の被りはダメ、エラーになる
・3文字以上、25文字以内じゃないとダメ、エラーになる

※𝙏𝙖𝙠𝙖𝙤🚚軽トラ爆走ゲーム開発中！@takaooooooo7さんにお教え頂きましたので追記
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/ef12734c-094c-eab5-a61d-f9feae21a62e.png)
Allow non-unique player display namesをオンにすると名前被りが許可されるようです。
<font color=#FF000000>がしかし、一度設定するとチェックを外せません…
試しにオンにしてSaveみましたが、Saveを押した時点でアラートすら出ずにそのまま設定されてグレーアウトされてしまいますので要注意。</font>


私は実装時に入力フォームの文字数を判定して3文字以上10文字以内でない場合に警告を出すようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/426d38bc-0337-6435-bf23-c2cc90b64cf5.png)
いつも通りですが、RequestをMake、Update User Title Display Nameというノードへ渡してあげます。
送信が問題なく処理された場合意外にプレイヤーネームを保存しても仕方ないかなぁということで送信成功時に変数にセットしていますが、特段なくても問題ないので割愛してもらっても問題ありません。
というか、OnSuccessとOnFailureにはカスタムイベントを繋がないといけないだけなので、その後の処理があるかどうかは関係なく動きますので、テストだけなら以下のような状態でも全く問題ないんですが一応。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/200760e9-8e4e-890e-13ae-c7741bf86501.png)

それでは送信。
PlayFabにリクエストを送る際には必ずログインしている必要がありますので、前回作ったログイン用イベントを実行してから今回のプレイヤー名送信を行います。
問題なく送信ができれば、SuccessUpdateの処理が走りますので今回であれば設定したプレイヤー名がPrintStringで表示されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/d30b239e-b759-ba70-00df-a0a98fc2b42d.png)
送信ができました、問題なさそうですね。

一応PlayFabで確認してみましょう。
管理画面の左側メニューからPlayersを選択、Searchを押すとプレイヤーが一覧で表示できます。
ログインが新しい順になっているので一番上のプレイヤーIDが送信したもので、IDのすぐ下に送信したプレイヤー名が表示されています。
また、Countryの項目がありますが、これは現在地からPlayFabが取得しているようなので自動で入ってます。
それでは読み込みに移ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/4198f6d2-b08d-21e7-a9dd-23aabbb42514.png)

# 事前準備
事前準備として、このあとの仕込みも兼ねて先程のプレイヤーでPlayFabへログインするようにしておきたいと思います。
ログイン部分のRequestデータにCustomIDを指定してログインするようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/55f6a0e3-102d-7ab3-ab4e-096da140a931.png)
CustomIDは上記のプレイヤー一覧で表示されたプレイヤーIDをクリックすることでプレイヤーデータへアクセスができますので、一番下のIdentities欄にあるものをコピーして使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/ff756fc5-52e1-db2c-d53d-2eeac301059f.png)


同時に、このままではプレイヤー名を設定したものの、ランキングにスコアが載っていない状態になりますのでスコア送信もしておきます。
一番上に来た方がわかりやすいので、500,000を送信しておきます。
一度プレイしてログイン→スコア送信をしておきましょう。

# Leaderboadのデータを取得してProfileを読み込む
さて、今回はプレイヤー名、スコア、国情報を読み込むようにしてみます。
が、そのままLeaderboadを読み込んでもプレイヤー名は読み込めますが国情報は読み込めません。
Leaderboadの情報を取得する際に含まれる、Profileの情報にはデフォルトで含まれていないからです。
そのため、MakeRequestをする際に、この情報も送ってほしい、と伝えてあげる必要があります。

Profile Constraintsというピンにjsonファイルで追加で取得する情報を入れてあげればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/31b6da3f-e498-bc84-9707-4d5b98845d23.png)
追加情報を取得するためのjsonファイルを以下のように作成します。
Requestを送る前にどこかで生成する必要があるので、BeginPlayなどで生成しておけばよいかと。

今回はDisplayNameとLocationsを取得したいので、
　1.ConstructJsonObjectでJsonを生成、変数にセット
　2.Set Bool Fieldノードを使ってField NameにShowDisplayName、有効化なのでBoolにチェック
　3.Set Bool Fieldノードを使ってField NameにShowLocation、有効化なのでBoolにチェック

あとはLeaderboadのRequestにこのJsonを含んであげましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/176d3bd2-0a31-4772-c3a4-cb4e4dea50f7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/90b11e5c-ed46-f31e-610f-918eb8ce6a52.png)

これで実行すればアウトプットログ上には以下のように取得ができているはずです。
``` ruby:Outputlog ※整形済み
LogPlayFab: Response : {
　"code":200,"status":"OK",
　"data":{"Leaderboard":[
　　{"PlayFabId":"6A6FB8B438FD1780",
　　　"DisplayName":"TESTPlayer",
　　　"StatValue":500000,
　　　"Position":0,
　　　"Profile":
　　　　{"PublisherId":"000000000000000",
　　　　　"TitleId":"5B531",
　　　　　"PlayerId":"6A6FB8B438FD1780",
　　　　　"Locations":[
　　　　　　{"ContinentCode":"AS",
　　　　　　　"CountryCode":"JP",
　　　　　　　"Latitude":00.0000,
　　　　　　　"Longitude":00.0000}],
　　　　　"DisplayName":"TESTPlayer"}},
~~~
```
Profile内にDisplayNameとLocationsが含まれていることが確認できました。
あとはこの情報から必要部分を取り出すだけですね。
といいつつ、自分で調べて実装までこぎつける為に、この取り出す、がどうしていいかわからずめちゃくちゃ時間がかかったのですが…。

全体はこうなります。
Jsonからデータを取り出す部分以外は同じなので割愛、取り出し部分に解説を入れておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/27f85c1b-2431-b19c-496c-d3647b7b0c91.png)
まず、アウトプットログを見てもらうと、Profileデータは配列であることがわかると思います。
更に、その中のLocationsも配列です。
これをどう取り出したらいいのかBlueprintの組み方がわからず物凄い時間を取られたのですが…
ものすごく分かりづらいので画像で説明します。
※StatValueとPositionについては前回と変わらないので今回は省きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/36af2919-c897-0c05-68cc-0f059c156278.png)
まずProfileを取り出し…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/80a4324b-d021-4c01-3821-e1f976402262.png)
Profileの中の配列、LocationsをGetObjectArrayFieldで指定して…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/7bbc2738-78e8-dd22-110b-a5eb9cb973e8.png)
何故かReturnが配列なので配列の0番目を指定してやっと目的のCountryCodeにたどりつくことができます…。
ここが本当に意味ワカラン状態でした。
あとはそれぞれ取り出したデータを前回同様、AsStringでStringに変換してあげればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/e88b6c57-8b57-ed4d-c261-50e38799d744.png)
では最後に確認、実行するとこうなるはずです。
表示ができていれば完了！

ただ、これまで同じ手順で進めてきて頂いた方は、プレイヤー名を設定したのが最後の一つのみですから、それ以外は表示されていないでしょう。
2行目のは別途テストで名前をつけたプレイヤーがいますがお気になさらず。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/a9d34176-3e4f-9801-0a42-85b76b4366ba.png)

# あとがき

実際のゲームで表示する場合にはプレイヤー名の設定を促す、もしくは設定しないとオンラインランキングにデータ送れませんよー！というようなアナウンスをしてあげるのがいいんではないかと思います。

また、今回はLeaderboardの上から100の取得でしたが、GetLeaderboadAroundPlayerノードを使用するとプレイヤーの順位の前後を取得することも可能です。
以下を一例として載せておきます。
※プレイヤー本人の順位の前後10名分を表示するような処理です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2285992/f18c47a6-98fb-26d3-5b5b-132176039c12.png)
PlayFabはものすごく多機能なので他にもかなりのことができるようですが、現在のところは自身がこのランキング系にしか使用していないのでここまで。
みなさまの参考になれば幸いです。