OculusのコントローラーにAndroidを使いたい話

## はじめに

この記事は[Oculus Rift Advent Calendar 2014](http://qiita.com/advent-calendar/2014/oculus-rift "Oculus Rift Advent Calendar 2014")の11日目の記事です。

どうも皆様、はじめまして。

盛大にOculusを使わない内容ですが、今後Oculsuのコントローラーをどうしていくかの一つの提案として書きたいと思うので、よろしくお願いいたします。

## 思い立ったきっかけ
Ocufesだったり、その他の発表会等に行きますと、色々とな面白い入力デバイスを皆様持ち寄られています。

たとえば、
![myo.jpg](https://qiita-image-store.s3.amazonaws.com/0/45441/dfd8d5e9-1042-eef3-86cb-83034047c234.jpeg)![kinectv2.jpg](https://qiita-image-store.s3.amazonaws.com/0/45441/fbe94175-c144-da1b-974a-7514a88ab17c.jpeg)![novint.jpg](https://qiita-image-store.s3.amazonaws.com/0/45441/6896eec3-8b9f-18ba-8d6f-e6235123776e.jpeg)

といろいろとありますが、一般の人が入手しようとすると、金額だったり海外から個人輸入しないといけなかったり、そもそももう生産中止だったりと、なかなか入手ハードルは高い状況です。

そんな中、[面白法人カヤック](http://www.kayac.com/)さんが作られた、[Oculus Witch](http://create.kayac.com/edge/oculus-witch/)では、iPhoneのジャイロを使い箒の角度等を取得するという方法が行われていました。

[Oculus Witch](http://create.kayac.com/edge/oculus-witch/)ではiPhoneを使用されていましたが、同じようなことをAndroidでやれると、

1. Androidは世代交代が激しくすぐに型落ちのデバイスが大量に出回るので安価に入手できる
2. できることの制限がiPhoneよりも緩いのでジャイロの入力としてだけじゃなくて他にも何かできそう
3. そもそもスマホとしてのAndroidじゃなくて組み込み系のAndroidとして考えたらコントローラー自作とか幅が広がりそう

というメリットが有るように思いました。

そこで、とりあえず、OculusのコントローラーにAndroidを使うための前段階として、UnityからAndroidをコントローラーとしてどうにかできないかの試行錯誤をしたので、その結果を書いていきたいと思います。

## UnityとAndroidデバイスの接続方法

### 使用する接続方法
現在のOculsuは基本的にはハイスペックなマシンで動くことを前提にされているため、デスクトップもしくはハイスペックノートPCにつながっているのが基本だと思います。

それらのPCは、それ自体を大きく移動させることを前提には作られていません。しかし、反対にコントローラーはユーザーが手に持って大きく動かせたりするなど自由度が高いほうがメリットが大きいです。
そうすると、ホストとなるUnityが動いているPCと入力機器となるAndroid端末の距離は自由度が高くとれる無線による接続が望ましそうです。

そこで、PCとAndroid端末の両方に対応していて、かつ使いやすい __Bluetooth__ を使用することにしました。

### 接続の大まかな流れ

PCとAndroid端末をBluetoothでペアリングする（この時SPPプロファイルが使える機器を使う必要がある）

　↓

Unityアプリがサーバとなり、Android端末からの接続を待つ

　↓

Android端末とPCをBluetooth経由で仮想シリアル接続する

　↓

Android端末から送りたい情報をひたすら送りつける

　↓

(ﾟдﾟ)ｳﾏｰ


### UnityでのBluetoothの受信方法
WindowsではBluetoothでSPPプロファイルを持つ端末とペアリングを行うと、自動で仮想シリアルポートが作成されます。
![comport.PNG](https://qiita-image-store.s3.amazonaws.com/0/45441/db2e5a1b-8af1-831b-31ea-9ee9a2e5b00d.png)

Unityからは、この仮想シリアルポートを簡単に使うことができます。

1. まずは、Unityのビルド設定のプレイヤー設定から、Api Compatibility Levelを「.NET 2.0」に変更します。
![net20.PNG](https://qiita-image-store.s3.amazonaws.com/0/45441/6d9e47b8-dcd6-a249-0f1a-6a2f0b31da26.png)
(標準だと「.NET 2.0 Subset」になっている)

2. System.IO.Portsを適当なスクリプトでインポートする
3. 下記のようなコードを書き、適当なオブジェクトに割り当てて通信待ちにする

```SerialServer.cs
using UnityEngine;
using System.Collections;
using System.IO.Ports;

public class SerialTest : MonoBehaviour
{
    private SerialPort serialPort = new SerialPort ("COM3", 9600);

    void Start ()
    {
        if (!serialPort.IsOpen) {	
	    serialPort.Open ();
	    serialPort.ReadTimeout = 1000000;  
	}
    }

    void Update ()
    {
        string line = serialPort.ReadLine (); // これが送られてきたデータになる
    }
}
```

### Android でのBluetoothの送信方法

Androidでは標準でBluetooth周りのAPIが整備されているので、下記のような流れでデータを送信できます。

1. Bluetoothが有効かどうかをチェックする
2. 有効だった場合に、ペアリングしているデバイスに対して接続し、コネクションを開く
3. 開かれたコネクションに対してデータを送りつける

```BluetoothClient.java
public class BluetoothClient {
    public static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    private  BluetoothSocket bluetoothSocket = null;
    private  BluetoothDevice bluetoothDevice = null;

    private InputStream in;
    private OutputStream out;

    private Context mContext;
    static BluetoothAdapter myClientAdapter;

    public BluetoothClient(Context context, String pcName) {
        mContext = context;
        myClientAdapter = BluetoothAdapter.getDefaultAdapter();
        Set<BluetoothDevice> pairedDevices = myClientAdapter.getBondedDevices();
        for (BluetoothDevice device : pairedDevices) {
            if (device.getName().equals(pcName)) {
                bluetoothDevice = device;
                break;
            }
        }
        if (bluetoothDevice == null) {
            return;
        }

        BluetoothSocket tmpSock = null;
        try {
            tmpSock = bluetoothDevice.createRfcommSocketToServiceRecord(SPP_UUID);
        } catch (IOException e) {
            e.printStackTrace();
        }
        bluetoothSocket = tmpSock;
    }

    public void connect() {
        try {
            bluetoothSocket.connect();
            in = bluetoothSocket.getInputStream();
            out = bluetoothSocket.getOutputStream();
        } catch (IOException e) {
            try {
                bluetoothSocket.close();
            } catch (IOException closeException) {
                e.printStackTrace();
            }
            return;
        }

    }
    public void write(byte[] buf){
        try {
            out.write(buf);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 注意点
いろいろ実際に動作させたり調べた限り、Android側で仮想シリアルポートのボーレートを変更することはできず、基本的に9600のようです。


## 野望
残念ながらまだ上記コードの安定動作化に成功していないのでまずはちゃんと普通のスマホ端末を安定したコントローラーとして動かせるようにしたいです。
最終的には、ゲーセンの登録カードみたいなノリで、自分のスマホにOculusの設定とかを入れといて、接続すると自分に最適化されたデータをいつでも引き出せるみたいなコントローラー＋αの機能を付けたいものです。

そういったこととは別に、スティック型Androidとか一般の人でもすぐに購入可能なTegraK1が搭載された開発ボードが売りだされたりとかで高性能な自作コントローラーが作れる土壌もあるので、そっちもやってみたいですね。
もし同じような事考えてすでにやられてる方とかがいればぜひともお声がけしていただければ！