Unityで自作デバイスを使ったモバイルアプリを作った話

最近作ってたゲームがある程度できたのでQiitaを書くいい機会だと思い投稿しました。

##どんなゲームを作ったか
ハードウェアを使ったスマホアプリを作りたかったので
チームで話し合った結果、イケメンで女の子を釣るゲームになりました
担当した箇所はBLEを使ったゲームとデバイス間の通信部分を担当しました。

####参照ツイート
https://twitter.com/riti_daichizaki/status/968517033594839040

<img width="150" alt="IMG_2284.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/190143/ed01817a-0daa-36a7-2aba-7879cea79e69.png"><img width="150" alt="IMG_2285.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/190143/369c8897-6f45-cca7-26e7-d915cdfec790.png"><img width="150" alt="IMG_2287.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/190143/8b0fc85c-cc42-69a7-222f-0a76d22a54bd.png">

画像はゆるドラシルの画像を使わせていただきました。
http://yurudora.com/tkool/
##開発環境
Unity

##今回の話のメイン

UnityのスマホアプリでBLEを使った開発の記事がほぼ皆無かつ
今回使ったAssetの日本語での解説が皆無なため
自分へのメモを兼ねて書きます。

##1.今回使ったAssetの紹介


###Bluetooth LE for iOS, tvOS and Android

https://assetstore.unity.com/packages/tools/network/bluetooth-le-for-ios-tvos-and-android-26661

iOS,tvOS,Androidに対応してBLE通信用Assetです。
とても安価なのでPlugin開発が面倒な場合とてもオススメです。

##2.実装
今回のAssetは突っ込んだらそのまま使えるなどの仕様ではなかっため
サンプルを参考にperipheralからreadするためのスクリプトを書きました。

```c#:qiita.c#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BLEManager : MonoBehaviour{
    enum States{
        None,
        Scan,
        ScanRSSI,
        Connect,
        Read,
        Disconnect
    }

    public string   DeviceName          = "デバイスの名前";
    string          ServiceUUID         = "サービスUUID";
    string          ReadCharacteristic  = "キャラクタリスティックUUID";

    BluetoothDeviceScript bds;

    string _message;

    public string GetMessage(){
        return _message;
    }
    
    private bool _connected = false;
    private float _timeout = 10f;
    private States _state = States.None;
    private string _deviceAddress;
    private bool _foundReadID = false;
    private byte[] _dataBytes = null;
    private bool _rssiOnly = false;
    private int _rssi = 0;


    // Use this for initialization
    void Start(){
        BluetoothLEHardwareInterface.Initialize(true, false, () =>{

            SetState(States.Scan, 0.1f);

        }, (error) =>{

            BluetoothLEHardwareInterface.Log("Error during initialize: " + error);
        });

        bds = GameObject.Find("BluetoothLEReceiver").GetComponent<BluetoothDeviceScript>();

    }

    // Update is called once per frame
    void Update(){
        if (_timeout > 0f){
            _timeout -= Time.deltaTime;
            if (_timeout <= 0f){
                _timeout = 0f;

                switch (_state){
                    case States.None:
                        break;

                    case States.Scan:
                        BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, (address, name) =>{

                            if (!_rssiOnly){
                                if (name.Contains(DeviceName)){
                                    BluetoothLEHardwareInterface.StopScan();

                                    _deviceAddress = address;
                                    SetState(States.Connect, 0.5f);
                                }
                            }

                        }, (address, name, rssi, bytes) =>{

                            if (name.Contains(DeviceName)){
                                if (_rssiOnly){
                                    _rssi = rssi;
                                }
                                else{
                                    BluetoothLEHardwareInterface.StopScan();

                                    // found a device with the name we want
                                    // this example does not deal with finding more than one
                                    _deviceAddress = address;
                                    SetState(States.Connect, 0.5f);
                                }
                            }

                        }, _rssiOnly); // this last setting allows RFduino to send RSSI without having manufacturer data

                        if (_rssiOnly)
                            SetState(States.ScanRSSI, 0.5f);
                        break;

                    case States.ScanRSSI:
                        break;

                    case States.Connect:
                        _foundReadID = false;

                        BluetoothLEHardwareInterface.ConnectToPeripheral(_deviceAddress, null, null, (address, serviceUUID, characteristicUUID) =>{
                            BluetoothLEHardwareInterface.Log("connectToPeripheral");


                            if (IsEqual(serviceUUID, ServiceUUID)){
                                _foundReadID = _foundReadID || IsEqual(characteristicUUID, ReadCharacteristic);

                                if (_foundReadID){
                                    _connected = true;
                                    SetState(States.Read, 2f);
                                }
                            }
                        });
                        break;
                    case States.Read:
                        BluetoothLEHardwareInterface.ReadCharacteristic(_deviceAddress, ServiceUUID, ReadCharacteristic, (message, messageChar) =>{
                            _message = bds._message;
                        });

                        _timeout = 0.1f;

                        break;

                    case States.Disconnect:
                        if (_connected){
                            BluetoothLEHardwareInterface.DisconnectPeripheral(_deviceAddress, (address) =>{
                                BluetoothLEHardwareInterface.DeInitialize(() =>{

                                    _connected = false;
                                    _state = States.None;
                                });
                            });
                        }
                        else
                        {
                            BluetoothLEHardwareInterface.DeInitialize(() => {

                                _state = States.None;
                            });
                        }
                        break;
                }
            }
        }
    }

    bool IsEqual(string uuid1, string uuid2){
        if (uuid1.Length == 4)
            uuid1 = FullUUID(uuid1);
        if (uuid2.Length == 4)
            uuid2 = FullUUID(uuid2);


        return (uuid1.CompareTo(uuid2) == 0);
    }

    string FullUUID(string uuid){
        return "0000" + uuid + "-0000-1000-8000-00805f9b34fb";
    }

    void SetState(States newState, float timeout){
        _state = newState;
        _timeout = timeout;
    }
}
```
他のオブジェクトからGetCompornetするとかしてGetMessage()を実行してあげると
メッセージを取得することができます。

##終わりに
BLEを使った開発はSwiftとか書くのは楽なのですが、
Unityを使った場合はPlugin依存なってしまいます。
しかも情報がほぼ皆無。

Unityの可能性はまだまだあると思うので公式でもっとIotに対応して欲しいと感じました