【Android】話題のパズルゲーム「イータイルズ」に通信対戦を実装した話【Google Play Games】

イータイルズはオフラインでも、リーダーボードやアチーブメントによって面白いゲームだとは思いますが、スマホゲーである以上はネット対戦が出来ないとトロールということになります。そこで、Google Play GamesのReal-time Multiplayerという機能を使って、ネット対戦を実装しました。結構本気でやって、大体一週間かかりました。

当初の想像どおり、プレイ人口が少ないのでロクにマッチすることはなく、ただ作ってみて楽しいだけになってしまいましたが、将来的に招待プレイ(他のプレイヤーを招待出来る。そのプレイヤーがゲームを持ってない場合はインストールを促せる)などの機能を使ってプレイ人口が増えるきっかけにはなると思いますし、何が出来るか何が出来ないのかはっきりさせる意味でも実装した意味はあったかと思います。

将来的には、レーティングを実装して、データベースの情報からその人の知能指数を計算して表示するような地獄のような世界に到達したいと考えています。頭の悪い人間が頭の悪いことを自覚し、絶望し、抑圧的に生きてくれることで社会がよりハッピーになるといういわゆるGAA理想主義に基づいています。また、イータイルズで特定の点数をとれない人間は運転免許を取得させないなどの施策により、交通事情はより高速に、そしてより安全になります。イータイルズによって選ばれた人間のみによって構成された世界を一緒に作りましょう。

ぜひプレイしてみてください。Android限定です。iOSは糞。

https://play.google.com/store/apps/details?id=jp.gr.java_conf.etiles.android&hl=ja

Real-time Multiplayerについて

真に理解するためには、

Real-time Multiplayer  |  Play Games Services  |  Google Developers

と、Androidでの実装方法について死ぬほど読んでもらうしかないです。しかし、肝心のドキュメントには誤りもあり、それは全部フィードバック(かなり頭に来てたため、時々ファッキンなんとかと書いたかもしれないですね)しましたが、現状で修正されているかは知りません。うんざりしたので、確認する気すら起こらないです。

概ね以下のような概念です。

ユーザはそれぞれ独立した部屋を作ります。そして、フルメッシュのP2Pネットワークを構築します。これらの部屋を作ったりコネクションを張ったり部屋を去ったした時に、特定のコールバックが呼ばれます。部屋を去る時には、コネクション自体も破棄します。マッチングの方法は自動マッチングによるもの、招待によるものがあります。自動マッチングには排他ビットを指定することが出来ます。これは「私はこのロールしかしたくありません」という場合に使うことが出来ます。また、部屋には整数型のVariantを設定することが出来て、これは「私は自分と同じVariantの部屋としか接続したくない」を表明出来ます。これは、イータイルズであれば、2x2でゲームする人は2x2を選択してる人とだけマッチするということを実装することに使えます。Variantの別の用途は、バージョン番号を混ぜ込むことによって通信の互換性を保つことが容易になります。

React NativeとNativeのやりとりについて

Native側を主として、RN側を従とする設計にしました。つまり、ゲームの開始やゲームの終了などRN側の変化はすべて、Native側からのイベント送信によって行われるということです。例えば、イータイルズではゲームを途中で投了する場合、まずNative側で部屋を抜ける関数を叩きます。これによって、onLeftRoomというコールバックが呼ばれます。ここから、RN側にleftGameというイベントを飛ばしています。これをRN側で捕捉して、投了する操作を実行しています。先にRN側を勝手に破棄してしまうと、Native側を破棄する人がいなくなってしまいます。

本来、Nativeだけで成立する話を、部分的にRNに切り出したと考えれば、自然な設計と思います。

ゲームの開始は、onRoomConnectedというコールバックを使っています。ここでVariantから混ぜ込んだボードサイズの情報を引き剥がして、同様にstartGameというイベントと一緒にRN側に飛ばしています。

まだ出来ていないこと

厳格な勝ち判定

イータイルズにおいて勝ちというのは、自分が相手より先に10回クリアすることです。あるいは、オプションとして、相手が途中で部屋を抜けた時には勝ち確にしても良いかも知れません。相手がいないとわかってるのに続けるのはバカバカしいですから。

しかし、これを厳格に行う方法が見つかりませんでした。理由は、片方の接続が切れた場合にもう片方の接続も自動的に切れる仕組みだからです。そのため、途中でトンネルなどに入ってたまたま接続が切れたのか、本当に抜けたのかなどの判別がつきません。

レーティングを考えた時には、これを厳格に行うことは必須になります。今のところ、囲碁や将棋の手合のように、勝った方が印をつけるという仕組みを採用しようと思っていますが、2人が同時に勝ち名乗りをした場合にどうするかなど考える必要があります。

もちろん、グリッチが存在しないようにする必要があります。イータイルズには昔、グリッチがありました。それは、ゲームを一度抜けてからシステムの時計を巧妙に操作した上でゲームを終わらせるとものすごい記録が出るというものでした。今は、ゲームを抜けた時点で記録を無効化するという厳しいペナルティを与えています。同様に、ネットワークが故意か過失に関わらず切れた場合、勝ち名乗りを無効化するというのも考えられますが、上に述べたように、相手が抜けた場合にはこちらの接続が切れるので、メガンテが成立することになり、これは使えません。せめて、どっちが原因で切れたのかということが分かればいいんですが。

レーティング

現在イータイルズはfirebaseを使っており、レーティングもplayer idと紐づけてfirebaseのfirestoreに入れてしまおうかと思っています。調べていないですが、たぶん、集計なども出来るでしょう。

問題1は、グーグルプレイのアカウント情報とplayer idでしか紐付いていないため、Gamer IDなどを取得出来ないか、あるいはいちいち取得することになってめんどうになることです。グーグルプレイの方でどうせリーダーボードのような機能があるのだから、レーティングのようなものも提供してくれてもいいと思うのですが、存在しません。(ファック)

問題2は、問い合わせごとに課金されるため、実装によっては問い合わせばかりされた場合に私が破産することです。更新については、現状のリーダーボードへの送信と同様に、リワード広告を見せたあとにしか出来ないということにすればマイナスになることはありませんが、問い合わせについては無限に実行された場合、死にます。悪意のあるユーザがそれを自動的に行うことも考えられます。もちろん、firebaseの無料プランでは一日5万回の問い合わせをキャップとしてそれ以上は実行されません。だから、しばらくは大丈夫なのですが、規模が大きくなってしまった時に破綻する可能性があります。

そのため、firestoreがどの程度出来るものなのか慎重に検討した上で、最悪ケースでは自作まで考える必要がありそうです。

グーグルプレイが提供してくれるのがどう考えても最速だと思うのですが。

何かアイデアがある人はTwitterでご連絡ください。

comments powered by Disqus
Built with Hugo
テーマ StackJimmy によって設計されています。