BLEを使ったゲームの例と、実装方法や問題点を紹介します。
この記事の想定読者
- Twinkrunの大ファン(◯)
- スマートフォンを利用したスポーツゲームを作りたい人(◯)
- Bluetooth Low Energy(BLE)に興味がある人(△)
- BLEの詳細仕様が知りたい人(×)
Twinkrunとは
Twinkrunとは、僕と大学の愉快な仲間たち(@mactkg, @62grow2e, @tkhs256)が2013年に開発したiOSアプリで、ひとことで言うと「夜の公園で頭にiPhoneをつけて大勢で走り回るゲーム」です。ボッチでは遊べません。
読み方は「ツインクルン」でも「トゥインクルン」でも無くて「Twinkle + run」なので、**「トゥインクラン」**です。
準備
推奨プレイ人数は4~8人くらいです。2人でも遊べますが、あまり面白くありません。暗い場所でないと画面が見えないので夜の公演でプレイするのがオススメ。プレイヤーはベルトで頭にiPhoneを固定します。ベルトは、アームバンドを改造して作ります。アームバンドはAmazonなどで手軽に入手することができます。商品の中には改造せずに頭に着けられるような長さになっているものもあります。
アームバンドのおすすめはこれです。(僕の頭囲であれば改造せずに装着できました)
ルール
ゲームを開始すると、画面の色が3秒おきに赤・緑・黒のどれかに代わります。画面が赤の人に近づくとスコアが吸い取られ、緑の人に近づくとスコアが奪い取れるというルールです。距離が近いほどスコアの上下も大きくなります。30秒繰り返してスコアが高い人が勝利です。言葉で説明しても分かりにくいので是非動画をご覧ください。プレイ後には自分のスコアの推移やランキングを見ることができます。
戦略
頭にiPhoneを付けるため、自分の画面の色は分かりません。そのため、相手の動きから自分の色を予想して次の行動をする必要があります。また、相手が赤なのに敢えて近づいてみるなどの駆け引きも生まれます。
なぜ今この記事を書くか
つい最近までiOS App Storeで公開していましたが、お布施が払えなかったため、消されてしまいました。それから色々あってAndroid端末を購入して、気まぐれでAndroid版を作っていますのでその近況報告などをしたいと思います。
iOS版の実装
用語など
余裕があれば追記します
実装について
スコアの奪い合いを実装する際に距離測定をする必要があります。Twinkrunでは距離をBluetooth Low Energy(BLE)の受信信号強度(RSSI)で測定しています。
BLEのコネクションは行わずに一方的にプレイヤー名などが入ったデータをアドバタイズしています。コネクションをしないのは以下の様な理由です。
- 1個のマスターに対して複数のスレーブが接続することはできるが、マスターとスレーブに同時になることができない
- 1つがマスターになれば良いが、(他にサーバを立てていないので)どの端末がマスターになるか決定できない
- (以上の問題が解決したとして)コネクションに時間が掛かる印象があった
- これは実装が悪かったりもあるんでしょうが、よく言われている10msec以内には出来ないなあという記憶
言い訳: サーバを立てれば(この後紹介する問題も)解決するんですが、開発機が研究室にあったiPod touch(6台くらいあった)で、遊ぶ場所は外なのでネットワークに繋ぐのが面倒くさかったBLEオンリーで作ることに夢を感じたのと、コネクションをすると色々闇に引っかかりそうなので、アドバタイズだけでやるのが安定してて良いのかなと思っています。
Twinkrunはプレイヤー名と画面に表示される色の順番を生成するためのシード値をアドバタイズメントパケットのLocal Name(Device Name)にカンマ区切りで入れています。これを受け取った端末はそのシード値とゲーム開始からの経過時間で他の端末の色を取得します。そのため、Twinkrunでは開始時刻を他の端末と同期させるために同時にスタートボタンを押すということをしています。
Local Nameに入れるプレイヤー名は端末に表示するときにユーザがわかりやすくするためのもので、どの端末から発信されたものかを特定するために入れているわけではありません。プレイヤー名で端末の特定をするとプレイヤー名が重複した時や、プレイヤー名を変えて再度アドバタイズしたときに端末を特定できなくなってしまうので、Local Nameなどに端末IDなどを突っ込むなどが考えられますが、Local Nameには限られた文字数しか入れることができないので、プレイヤー名とシード値を入れるのが精一杯で、十分なスペースがないです。CBPeripheralのidentifierを使って端末をidentifyします。
TIPS: この実装に至るまでの過程
個人的な感想ですが、外で遊ぶゲームを考えようとするとGPSを使った位置計測をしがちですが、動きのあるスポーツゲームのデザインをするのは難しいと思います。次に考えるのは加速度センサですが、使えるのは動いているか動いていないかの情報くらいです。加速度センサや地磁気センサを使って位置推定とかもやってみましたが、素早く動くとノイズが乗りまくって使い物になりません。BLEは不正確ながらも相対的な距離をそれらと比較して安定して取れるので他にも面白いゲームが作れそうです。
Android版作成中の問題
Android版を作りたくなったのでじんわりと最近開発を進めているのですが、iOSとAndroidでBLEを使おうとする際に色々な問題にぶち当たります。
バージョンの問題
AndroidのBLE開発でまず気にすることは対応している端末が少ないということです😂。Android 5.0以降でないとPeripheralになれません。そして殆どの古い端末はAndroid 5.0に対応していません。
機能 | 使えるバージョン |
---|---|
Central | 4.3以降 |
Peripheral | 5.0以降 |
解決方法
Nexus 5でもPeripheralに対応していないという話がありますので、Android 5.0に対応していればいいというわけでは無さそうです。よしなに端末を手に入れましょう。安いものであれば1万円程度で買えるようです。
identify出来ない問題
iOS側のCBPeripheralにはペリフェラルがどの端末から発信されたのか識別するためにidentifierがありますが、AndroidのBLEではstartAdvertiseをするたびに違うものが入れられてしまうので、iOSではAndroidからアドバタイズされたデータをidentifyできません。Android同士ではMACアドレスが読めるためそれでidentifyすることが出来ますが、iOSではペリフェラルのMACアドレスを読む手段がありません。
解決方法
Local Nameには短縮名と完全名があり、完全名は最大で248byte入る(Device Name Characteristic)らしいので、それを活用すればCBPeripheralのidentifierやMACアドレスを知らなくても十分な長さのIDをアドバタイズメントパケットに含めることが出来そうですが、AndroidのBluetoothAdapter#setName()で8文字以上をセットすると何故か落ちます。なぜ...。
というわけで、Local Nameに入れるものはユーザIDくらいにしておいて、サーバからプレイヤー名やら色のシード値やらを取ってくるのが一番良い解決方法かなあと思います。つまらないオチですが。
BLEオンリーで解決するための良いアイデアがあれば教えていただきたいです。
まとめ
スコープが狭い記事でしたが、走り回って駆け引きもあるような面白いゲームを作りたい誰かの参考になれば嬉しいです。(超人スポーツというのも最近あるようです)
Android版が完成したらApple様にも再びお金を払って同時公開したいなーと思っているのでこちらの応援もよろしくお願いします。🖖
参考
現在であればiOS×BLE本があるのでより手軽にBLEについて知ることができますが、2013年は流行りだした頃だったので、わふうさんのBLE解説記事がとても参考になりました。大変感謝しております。