【自前DB】ニフクラ mobile backendがサ終するらしいのでオンラインランキング自作した話
目次
はじめに
こんにちは!
15、16日の2日連続実世界コーディング課、HayatoIです!
昨日はゲーム紹介でしたので、今日は少し技術的な話をしようかと思います。
目次
- Unityオンラインランキングについて
- 環境・必要なもの
- 実装
- 注意事項
- データベースの作成
- PHPでのサーバ側処理
- スコア送信
- ランキング取得
Unityオンラインランキング
みなさんもunity1weekのゲームなどを遊んだ際、このようなランキング画面を見たことがあるのではないかと思います。
実はこれ、ないちさん(https://twitter.com/naichilab)という方が作ってくれたライブラリGitHub(https://github.com/naichilab/unity-simple-ranking)からパッケージをインストールすることで簡単にほぼノーコーディングでオンラインランキングを実装できるという非常にありがたいものです。
しかしこのオンラインランキングですが、バックエンド(サーバー側)でニフクラ mobile backendを使用しているため、今回のサービス終了に伴って2024年3月31日以降利用できなくなります。(別のバックエンドに以降したバージョンを作ってくれる可能性もあると思いますが)
ランキングの実装
このようにAPIは便利な一面、サービス終了などで突然使えなくなってしまうリスクがあるわけです。
私が作成してきたゲームもニフクラを利用しており、移行作業が非常に大変でした。
もうこのような移行作業をしたくないので、「自前のサーバーを用意してそこでランキング管理しよう」となったわけです。
さて、実際にオンラインランキングを実装していきましょう。
DBサーバーですが、基本的に何でもOKです。
サーバーの設定が面倒で楽したい方は少々料金がかかりますが、さくらサーバー、XサーバーなどのDB機能付きレンタルサーバーがおすすめ。自分で色々サーバーをカスタマイズしたい方はAWS(初学者は難易度高めです)。とりあえず動かしてみたいんだ、って人は恐らくローカルサーバー(XAMPPなどで比較的容易に構築はできます)でも大丈夫でしょう。ただ、セキュリティ面で公開用に使用するのはおすすめしません。
Raspberry Piを用いて自宅に簡易サーバーを立ち上げる猛者もいますが、話がそれるのでこの話はまた今度・・
必要な前提知識は以下の通りです。少し難易度高いかも。(知らない部分は各自調べてみてから読むと理解しやすいはず。1回生の人は恐らく無理かもごめん。)
- 非同期処理(コルーチンorUniTask)
- UnityWebRequest
- SQL言語(select、insert、updateくらいでOK)、PHP言語(if、forなどの基本的文法+POST変数など)
- サーバの設定、FTPソフトの使い方(この辺は大きく環境に依存するので全省略します)
実装環境
- Unity 2022.3x
- UniTask(非同期処理に使用、コルーチンでも大丈夫)
- EasySave3(クライアント側の主キー保持に使用、もってない人はJSONとかでやってください)
- DBサーバー
- Linux
- apache 2.4.x
- MariaDB 10.5.x(MySQLでも多分いける)
- PHP8.1.x
- FTPソフト(サーバからファイルのダウンロード、アップロードを行うソフト、私はTransmit5を使用しています)
注意事項
今回紹介するランキング処理は、できるだけ簡潔に説明するため、システムのベースとなる部分しか紹介しません。
そのため実際に運用する際にはセキュリティなど、不十分な点がいくつかあります。
実際に運用する際は各自で学習し、自己責任で行うようにしてください。
データベースの作成
では、まずサーバサイドから順に作って行きましょう。
実際のデータがわかりやすように、今回はPHPMyAdminを使用します。
PHPMyAdminを使えば、テーブルやビューの作成をGUI上で行うことができるので便利です。
では、データベースをサーバに作成(環境依存なので省略)した後、今回使用するテーブルを作成します。
テーブル名は “sample_table”としました。カラム数はID、名前、スコアを管理したいため、3としました。
こんな感じでいいでしょう。user_idが主キーです。
データベースでやることはこれだけです。簡単でしょう?
PHPでサーバ側処理を書く
続いて、PHPでサーバ側の処理を作っていきます。
今回はSQL分もPHP上で記述していくので、ここでデータベースの操作処理を書いていくことになります。
今回はDB接続、スコア送信、ランキング取得の3つのPHPファイルを作成します。
まずはDB接続の処理から。connectDB.phpとしました。
DBへの接続はPDOを使用します。PDOとは、「PHP Data Objects」の略で、PHPからデータベースへ簡単にアクセスするための拡張モジュールです。隠してある部分には先程作成したサーバーのドメイン(ローカルの人はlocalhostから始まるはず)やそれにアクセスするための権限を持ったユーザを記述します。
今回は記述していないのですが、セキュリティの面で、認証キーのようなものを作成し、ハッシュ関数で比較して異なればreturnをして、それ以降の処理を行わせないようにするなど、実際に運用するには少し不十分だと思います。この辺は話すと長くなりそうなので各自で調べてみてください。(password_hash関数辺りで検索)
続いて、スコアの送信処理です。
まず初めに、先程作成したconnectDBでデータベースとの接続を確立し、その後POSTで送信されてきた値によって処理を変えています。後ほど説明しますが、ユーザーの初期IDを0としたため、IDが0の場合は初めてのユーザーからの送信と認識し、INTERT文を実行しています。user_nameの部分は、SQLインジェクション対策として、プレースホルダ処理を行っています。本当はすべての変数に行うべきですが、今回は特に公開しないのでこれでいいでしょう。(名前はユーザが直接入力できる部分なのでここだけは必ず実装しましょう。)
$returnIdの部分は、初めてランキングを送信したユーザは自分のIDがわからないので、lastInsertId()を使用して、それを返すことでクライアント側に「あなたのIDはこれだよ」と教えてあげて、次からはこのIDで送信できるようにしています。
ランキングの取得処理です。あまりコード良くないかも。
処理としては、降順か昇順かで処理を変え、そのSQL分を実行し、その結果をforeach文で返しています。この返ってきたメッセージをUnity側で受け取り、ランキングを表示させます。ここも本当はプレースホルダ実装すべきです。
DESCの部分は降順か昇順によって変えましょう。私が実際に運用しているものでは、POSTにASCかDESCを指定するパラメータを用いてどちらでも対応できるようにしています。
PHPでの記述は以上です。
・ちょっと脱線
今回はサーバ側処理にPHPを用いていますが、実は近年PHPは徐々に勢力を弱めつつあります。
最近のトレンドはNodejs辺りかな。非同期であったり、フロントからバックまですべてJavaScriptで統一的に書けるという優れものです。PHPに比べ環境構築などは面倒なんですがバックエンドに興味ある人は是非。
Unity側の実装
やっとUnity側の処理です。
がっつりUnityWebRequestとUniTaskを使用しますので、知らない方は少し調べてから読んだほうが理解しやすいと思います。
まず、送信処理について作っていきましょう。
スコアの送信
DBUpdateというクラスを作りました。重要な部分のみ解説しますね。
まず、「サーバーとの通信を待つ」のでこの関数は非同期処理となります。
次に、WWWFormというものがあります。
JavaScript触ったことある人はFormDataと同じ、Unity使ってる人はDictionaryのようにAddFieldでPOSTパラメータを追加できる、とおぼえましょう。
次にUnityWebRequestを使用してサーバーと通信を開始します。
usingステートメントを使うと、リクエストのDisposeをC#が自動でやってくれるのでおすすめ。using使わない場合は、通信が終わり次第request.Dispose()でリソースを開放してあげましょう。
その後、Post()で送信する準備をし、SendWebRequest()で通信を開始します。
では、実際に送信してみましょう。
PHPMyAdminの管理画面はこうなりました。ちゃんと送信できていますね。わーい!
さて、実際に運用する場合なのですが、送信する際、クライアント側は最低限自分のユーザIDを知っている必要があります。自分のユーザIDを知らないと、どのIDに対して更新を行うかわからないため、すべてのスコアを記録するならいいのですが、ハイスコアの更新ができません。
更新処理を行いたい場合、3つしないといけないことがあります。
・PHP側で、ユーザIDが同じだった場合のUPDATE処理
・初めての送信だった場合、ユーザIDをクライアント側に知らせる処理
・クライアント側で自分のユーザIDを記録しておく機能
少しだけ解説します。まず1つめですが、スコア更新処理のPHPにあるSQL文をUPSERTに変更します。簡単に言うとIDがあればUPDATE、なければINSERTする処理です。
MySQLだとON DUPLICATEっていうやつですね。
2つめは、同じく更新処理のPHP側で、新しい行に挿入した際、そのID(主キー)をechoなどで出力できるようにします。
LAST_INSERT_ID()とかで簡単にとれます。
3つめは、この出力したIDをUnity側で保存します。このIDを保存しておいて、次回以降の送信はこのIDで送信することで、サーバー側は「この前と同じユーザーだ」と認識できるようになり、スコアの更新処理を行えるようになります。IDはEasySaveなどのアセットかJSONで保存しましょう。
ランキングの取得
ランキングの取得をわかりやすくするために、データベースの状態を以下のようにします。
まず、ランキングデータを扱うためのクラスを作りました。今回は名前とスコアだけでいいでしょう。
ランキング取得処理です。複雑そうに見えますが、 データ取得後の扱いが少し長くなってるだけで意外と簡単です。
ランキングは1人のデータだけではないので、先程作成したRankingDtoのリストで返すようにしています。
今回は取得だけですが、セキュリティ的に私はいつもPOSTを使ってます。多分GETでも大丈夫です。
28行目からのfor文、もう少し良い書き方ないかなと悩んでおります。いい案あればぜひ教えてください。。。
さて、このようにして実際にデータベースへアクセスしてみましょう。
できました、DESC指定したので、ちゃんと降順(値が大きい順)に出力されています。やった〜〜
このデータをTextなどのUI上に表示することで、ランキングを実装することができます。
夏季休暇中に個人で作成したゲームです。実際に今回のようなデータベースを用いてランキング実装してます。遊んでみてね。
KeepEating – IH : https://unityroom.com/games/keepeating
さいごに
細かいセキュリティのことなどを考えなければ、結構簡単だったと思います。
最後まで読んでくれてありがとうございました。
もう一度言いますが、実際に運用する際はしっかりと理解した上で自己責任で行うようにしましょう。
以上、毎年自コースとは関係ないようなアドカレを書いている気がする実世界コース3回生HayatoIでした。
・昨日の記事、実際に私がDB運用しているRiG++発ソシャゲです。
https://rigpp.sakura.ne.jp/wp/?p=2355
・昨年の記事、よかったら読んでみてね
明日は2回生コーディングのko-kiです。
どうやらUnity上でのオブジェクトの配置を楽にする方法があるらしいですよ。お楽しみに。
RiG++メンバーへ
RiG++内での活動に限り、私の作成したランキングライブラリと、ティア・マテリアルで運用しているデータベースサーバーの余剰スペースを貸し出しますので、興味ある人はDMください。