OpenVRでトラッキングしているコントローラの種類を判別する

#はじめに
こんにちは、最近Indexコントローラを買ったZeniZeniです。
VRゲームをやっているとよく出くわすのですが、viveのコントローラで遊んでいるのにチュートリアルでのコントローラの説明がoculus touchで操作方法がわからない…という事態、できることなら全種類表示するか、今使っているコントローラに合わせて説明してほしいですよね。

今回はUnityにおいて、今現在使用しているコントローラの種類を動的に判別する方法を紹介します。

#動作環境
- Unity 2019.3.9f1
- SteamVR 2.5.0

#実装方法
結論から入りますが以下のコードを実装します。

```c#:DeviceTypeDetecter.cs
using UnityEngine;
using Valve.VR;

public class DeviceTypeDetector : MonoBehaviour
{
    //defaultのやつにしておく
    public SteamVR_Action_Pose poseAction = SteamVR_Input.GetAction<SteamVR_Action_Pose>("Pose");

    //取得したいデバイスが割り当てられている入力元（Left Hand等）
    public SteamVR_Input_Sources target;
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.D))
        {
            var deviceName = DetectUsingDeviceType(target);
            Debug.Log(deviceName);
        }
    }

    public string DetectUsingDeviceType(SteamVR_Input_Sources target)
    {
        CVRSystem system = OpenVR.System;

        uint index = poseAction[target].trackedDeviceIndex;
        //targetに設定されているデバイスが存在しないとき-1が返ってくる
        if (system == null || index == -1) return null;

        ETrackedPropertyError error = ETrackedPropertyError.TrackedProp_Success;

        //第2引数に入れるETrackedDevicePropertyという列挙型で、取得したい情報を選べる
        uint capacity = system.GetStringTrackedDeviceProperty(
            index,
            ETrackedDeviceProperty.Prop_ModelNumber_String,
            null,
            0,
            ref error);
        
        //targetのデバイスの名前が取得できなかった時
        if (capacity <= 1)
        {
            Debug.LogError("<b>[SteamVR]</b> Failed to get Model Number for tracked object " + index);
            return null;
        }
        
        var buffer = new System.Text.StringBuilder((int)capacity);
        
        system.GetStringTrackedDeviceProperty(
            index,
            ETrackedDeviceProperty.Prop_ModelNumber_String,
            buffer, //bufferにデバイスの情報が書き込まれる
            capacity,
            ref error);

        return buffer.ToString();
    }
}
```

これを適当なゲームオブジェクトにアタッチします。
インスペクターだとこんな感じです。
![bandicam 2020-04-02 20-34-17-709.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281560/8a9484ff-ed93-d76d-b749-8bee6cf627eb.jpeg)

ゲームを実行し、コントローラがトラッキングされた状態でDキーを押すとコントローラの名前がコンソールに出力されます。
このとき出力されるコントローラの名前ですが、`GetStringTrackedDeviceProperty`関数の第2引数に入れる[ETrackedDeviceProperty](https://valvesoftware.github.io/steamvr_unity_plugin/api/Valve.VR.ETrackedDeviceProperty.html#fields)という列挙型の種類によって、微妙に異なる名前が返ってきます。
[ETrackedDeviceProperty](https://valvesoftware.github.io/steamvr_unity_plugin/api/Valve.VR.ETrackedDeviceProperty.html#fields)には大量に種類があり、コントローラの名前だけでなくバッテリー情報や使用しているトラッキング方法などの情報も取得することができます。
この中でコントローラの種類を判別することができるものは3種類あり（実はもっとあるかもしれませんが調べるのがつらかったのでとりあえず3種類説明します）、各コントローラとそれによって出力される値は以下の表の通りです。
![bandicam 2020-04-02 21-54-52-611.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281560/3ed3abb6-53d4-a18c-c16f-2577b812fc2f.jpeg)

(その他のコントローラについてはご自身で調べてみてください。)

後はこれらの値を用いてよしなに分岐処理を入れるだけです。

#余談
Prop_RenderModelName_Stringによって参照される値は、SteamVR_RenderModelという関数内でコントローラのモデルをUnity内に表示するために使われています。
コントローラのモデルはどこから持ってきているのだろうかと調べたところ、例えば左手のindexコントローラならば
`\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\indexcontroller\resources\rendermodels`フォルダ内に大量のobjファイルが入っており、ここからロードしていたことがわかりました。
![bandicam 2020-04-02 21-41-54-394.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281560/df816e33-77c2-8806-60ff-92f110fd0efa.jpeg)


#参考資料
[OpenVR Documentation](https://github.com/ValveSoftware/openvr/wiki)