前に BLE(Bluetooth Low Energy) ライブラリを使って実装してみようとしてました。
ライブラリはサンプルプロジェクトを見ながらだとわりと簡単に使えました。
動作確認しても特に支障なく使えたんですが、なにかアプリを作ってみようかと思った時に問題があって使えませんでした。
今度は色んな用途で使えるように基本的なAPIだけを使って実装してみます。
問題
そもそもスマホがあまりBLE対応していない。 テスト機の2/5しか対応してなかったです。 格安スマホや1世代前のスマホだと使えなさそうです。
AndroidとBluetoothのバージョンを気にしないといけないのは面倒です。
ちなみにあんまりiPhoneなら気にしなくてよさそう。
そもそも必要性があるときに機能を作ればこんなことには…。
実装の概要
基本的なBluetooth実装は以下を見れば簡単にできる。
これをUnityで使えるようにしてみます。
まずアクティビティとコンテキストはUnityのものを使う。
1 2 3 4 |
import com.unity3d.player.UnityPlayer; ... Activity activity = UnityPlayer.currentActivity; Context context = activity.getApplicationContext(); |
Unityからライブラリを利用するのは簡単で、こんな感じで呼び出す。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
AndroidJavaObject ajo = null, ajo2 = null; void Start () { #if UNITY_ANDROID ajo = new AndroidJavaObject( "jp.co.narumium.bluetooth.XXX"); ajo2= new AndroidJavaObject( "jp.co.narumium.bluetooth.YYY","AAA","BBB"); #endif } ... /* Staticの場合はXxxStaticを使う */ string str = ajo.Get <string>("publicVariable");//変数取得 ajo.Set <string>("publicVariable",str); //変数設定 ajo.Call("DoFunc"); //関数呼び出し string rtn = ajo.Call<string>("GetMac");//関数呼び出し(返値あり) |
Bluetoothはサーバースレッドとクライアントスレッドで動くので単純な返値で結果を取得できません。
UnityのUpdateでAndroidJavaObjectのパブリックフラグを監視することも考えたけど、UnitySendMessage でUnity側の関数を呼び出すことができた。
1 2 3 4 5 6 7 |
//Unity側ではコンストラクタで呼び出される関数を指定する ajo= new AndroidJavaObject( "jp.co.narumium.bluetooth.Util","オブジェクト名","関数名"); //ライブラリではコンストラクタ等で設定した関数にメッセージを送る private void SendUnity(String msg){ UnityPlayer.UnitySendMessage(unityObjName,unityFuncName,msg); } |
通知やデバッグのためにトーストは使いやすいですが、スレッドから呼び出すと強制終了してしまいました(何故か終了しない機種もあったので対応に手間取った)。
そこで順番に処理を呼び出すためにLooperにメッセージを送るようにします。
1 2 3 4 5 6 7 8 9 10 |
Handler handler = new Handler(activity.getApplication().getMainLooper()); ... private void ShowToast(final String str) { handler.post(new Runnable() { @Override public void run() { Toast.makeText(context, str, Toast.LENGTH_LONG).show(); } }); } |
基本的にはサンプル通り実装できたんですが、端末によってBluetoothの挙動が違うのでうまいこと使えるようにしないといけなかった。
具体的には検索可能にする(ACTION_REQUEST_DISCOVERABLE)処理が3通り。
- 探索不能から探索可能になる
- 探索不能から探索可能になって通知が出る
- 常時探索可能だが探索可能にしてよいかのConfirmがでる
3つ目が意味不明だし処理の邪魔になります。
相手がサーバである(acceptしてる)かどうか確認する方法が見つからなかったので、とりあえず全候補に connect してみてもいい(エラーハンドル必須)。
複数のサーバが存在する場合を考慮して、端末スキャンから複数候補をUnityに返してそっちで選択することで再度接続処理に移るのがよさそうです。
ここまでのフローはこんな感じになりました。
やっぱり1から作るなら色々と考慮することが多くて面倒…。
今回はここまでで上手いことまとめられたら次回に続く。
追記
常時検出可能な端末は、正確にはBluetoothをオンにしてから1度でもACTION_REQUEST_DISCOVERABLEにした場合に検出可能な端末でした。
当然ながら他の端末は指定した秒数だけ検出可能になります。