はじめまして、インターン生の河野です!この記事は、 私のインターン活動の主題である「SteamVR Knuckles EV3 コントローラを試す」において行った、Unity でコントローラの情報を取得する方法について書いたものです。
注意事項として、SteamVR Knukles EV3 コントローラは本来 SteamVR Unity Plugin v2 で使用するデバイスであり、この記事で紹介するSteamVR Unity Plugin v1 では完全には機能を使えません。その代わり「もし SteamVR Unity Plugin v1 で Knuckles EV3 を使ったら?」がわかります。このコントローラ概要や私のインターン活動の詳細は以下のサイトを見てください。
【SteamVR Knuckles EV3】手に”着ける”コントローラを試す!- Techno sports1. SteamVR Unity Plugin v1・v2 の概要
Unity で SteamVR を使用するには、SteamVR Unity Plugin v1・v2 を使用します。 SteamVR Unity Plugin v1はすでに終了し、現在は SteamVR Unity Plugin v2 がアップデートされていますが、それらにそこまでの互換性はなく、使い方は異なっています。
SteamVR Unity Plugin v2 は2018年9月頃にリリースされましたが、コントローラの制御方法が v1 から一新され、移行ができない人が続出しました(v2 では SteamVR Interaction System や Skeletal Input など、コントローラ・アクションを機能性高く簡単に(?)開発できるようにする体系が登場しました)。
ここで v1 のコントローラ情報取得方法を紹介する理由は、今回の私の活動においてわかった、最新版の SteamVR では 1PC に複数ペアのコントローラのアクションを取得できず、複数ペア使える昔のバージョンでは v2 が使用できないためです。その件については、別記事「【Unity×SteamVR】複数ペアのコントローラの情報を1PCで取得する方法 ~ダウングレード ~(2019年3月時点) – Techno sports」を参照してください。
ここでは、複数ペアの接続を仮定した上で以下について説明します。
- SteamVR Unity Plugin v1でのコントローラの情報取得方法
- 通常 VIVE コントローラと SteamVR Knuckles EV3 コントローラから取れる情報(中指~小指以外の情報)
情報取得方法については各サイトで方法が紹介されています。例えば、この方のサイト「Unity+HTC Vive開発メモ – フレームシンセシス」を見ればほとんどの情報が得られます。
2. コントローラを検知する
まずは、SteamVR でつなげたコントローラを取得します。SteamVR の Prefabs にある Controller (left)
と Controller (right)
がありますので、それを持ってきます。left と rightに意味はなくどちらも同じものなので、これを複製して3台目以降のコントローラを割り当てます(以下、このオブジェクトを Controller
と 呼称します)。[CameraRig]
の “SteamVR_ControllerManager.cs” は、2台(以上)のコントローラの自動検知をするものですが、2台以上だと Controller
と実際のコントローラがトラッカーも含めて種類混ぜこぜにペアリングされる場合があるので、ここではスクリプトを切ります。
コントローラのスクリプトは、Controller
についている “SteamVR_TrackerObject.cs” です。コントローラや LightHouse(Basestation) やトラッカーはデバイスIDで管理されていて、番号を指定すると、そのデバイスの位置・姿勢・アクションが取得できるようになります。その番号を指定するには、“SteamVR_TrackerObject.cs” が持っている SteamVR_Controller などの全クラスインスタンスに対して行う必要があります。そこで以下のスクリプトを Controller
にアタッチします( Start メソッドの中でデバイス番号を設定する必要はありません)。
public SteamVR_TrackedObject Controller; // Controllerオブジェクトをアタッチ or GetComponentで取ってくる
public int deviceID; // インスペクタ上でデバイスIDを設定するなら
private void Start() {
// SteamVR_TrackedObjectなどこのオブジェクトについているクラスインスタンスのSetDeviceIndexメソッドを引数deviceIDで一斉に実行して、デバイス番号をdeviceIDに設定
this.BroadcastMessage("SetDeviceIndex", deviceID, SendMessageOptions.DontRequireReceiver);
}
デバイス番号は SteamVR で表示されるアイコン順とも限らないので、自分で調べるか、番号を線形サーチしてコントローラかを自動判断させる必要があります。上記で取得したデバイスはデバイス名が取得できるので、欲しいコントローラかどうかを選択できます。
string modelName = GetComponentInChildren().renderModelName; // 今設定したデバイスの名前
// LightHouseかトラッカーだったら、コントローラでないので、オブジェクトを消す
if (modelName.IndexOf("basestation") >= 0 || modelName.IndexOf("generic_tracker") >= 0) {
Debug.Log(this.name + " basestation!");
this.gameObject.SetActive(false);
}
3. コントローラの情報を取得する
上の「コントローラを検知する」で用いた Controller
とスクリプトを引き続き用いて各コントローラの情報を取得します。
- 位置・姿勢 :
Controller
の Transform そのもの
コントローラの位置・姿勢情報は、“SteamVR_TrackedObject.cs” によってController
の Transform が更新されています。 - アクション:SteamVR_Controller クラスのインスタンスから取得
アクションを取る方法で一番簡単な方法は “SteamVR_TrackedController.cs” をアタッチすることです。各ボタンのイベントにメソッドを登録することでアクションを取得できます。ただ、取れるアクションは押した瞬間と離した瞬間であり、トリガのように継続的に押している・押し度合いが変わるものは最初の瞬間しかイベントが発火していないようでした。
そこで、各ボタンアクションを取得する関数が用意されているので、自前で情報を取得し、押している間の変化も検出するようにします。注意点は、プログラムの関数や引数の名前とは関係ないボタンやトリガが反応していたり、名前が似たり寄ったりな点です。間違えないようにする必要があります。
今回はイベントでなく、Update メソッドに直書きでフレームごとにアクションの判定をしています。ただ、できるならイベント駆動の方が拡張しやすいとは思われます。
private void Update(){
device = SteamVR_Controller.Input(deviceID);
// トリガ系
if (device.GetTouch(SteamVR_Controller.ButtonMask.Trigger)){ // (1)トリガを触っている間にでる(標準VIVEコントローラ)
Debug.Log("トリガーを浅く引いている:" + device.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger).x);
}else if (device.GetPress(SteamVR_Controller.ButtonMask.Trigger)){ // (2)トリガを触っている間にでる(SteamVR Knuckles EV3コントローラ)
Debug.Log("トリガーを深く引いている:" + device.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger).x);
}else if (device.GetPressUp(SteamVR_Controller.ButtonMask.Trigger)){ // (3)トリガを離した瞬間に出る
Debug.Log("トリガーを離した:" + device.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger).x);
}
// スティック系
if (device.GetPress(SteamVR_Controller.ButtonMask.Touchpad)){ // (4)スティックをクリックしている間に出る
var position = device.GetAxis();
Debug.Log("タッチパッドをクリックしている: " + "x: " + position.x + " y: " + position.y);
}else if (device.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad)){ // (5)スティックを離した(クリックをやめた)瞬間に出る
Debug.Log("タッチパッドをクリックして離した");
}
// タッチパッド系
if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Touchpad)){ // (6)タッチパッドを触った瞬間に出る
Debug.Log("タッチパッドに触った");
}else if (device.GetTouch(SteamVR_Controller.ButtonMask.Touchpad)){ // (7)タッチパッドを触っている間に出る
var position = device.GetAxis();
Debug.Log("タッチパッドに触っている: " + "x: " + position.x + " y: " + position.y);
}else if (device.GetTouchUp(SteamVR_Controller.ButtonMask.Touchpad)){ // (8)タッチパッドを離した瞬間に出る
Debug.Log("タッチパッドを離した");
}
// メニューボタン系
if (device.GetPressDown(SteamVR_Controller.ButtonMask.ApplicationMenu)){ // (9)メニューボタンを押した瞬間に出る
Debug.Log("メニューボタンをクリックした");
}else if (device.GetPressUp(SteamVR_Controller.ButtonMask.ApplicationMenu)){ // (10)メニューボタンを離した瞬間に出る
Debug.Log("メニューボタンを離した");
}
// グリップボタン系
if (device.GetPressDown(SteamVR_Controller.ButtonMask.Grip)){ // (11)グリップボタンを押した瞬間に出る
Debug.Log("グリップボタンをクリックした");
}else if (device.GetPressUp(SteamVR_Controller.ButtonMask.Grip)){ // (12)グリップボタンを離した瞬間に出る
Debug.Log("グリップボタンを離した");
}
}
以上のスクリプトで用いた関数は全体のうちの一部の関数です。そのほかにもアクションを取る関数は存在します。ただし、全ての関数でコントローラの情報が取れるわけでもなく、今回のコントローラについては以上のスクリプトの関数で十分です。
以下に、標準 VIVE コントローラと SteamVR Knuckles EV3 コントローラのアクションバインドを示します(番号は、上記スクリプトのアクションについた番号に一致します)。
標準 VIVE コントローラ | SteamVR Knuckles EV3 コントローラ | |
トリガ | (1)(浅く)押した (3)離した | (2)(深く)押した (3)離した |
スティック | (4)クリックした (5)クリックして離した (6)押した (7)押している (8)離した (タッチパッドを押し込んだ時に相当) | (4)クリックした (5)クリックして離した (スティックを押し込んだときのみ反応。 カチッと押し込みながらグリグリしないと反応しない) |
タッチパッド | (6)押した (7)押している (8)離した | (6)押した (7)押している (8)離した |
グリップボタン(Aボタン) | (9)押した (10)離した | (9)押した (10)離した |
メニューボタン(Bボタン) | (11)押した (12)離した | (11)押した (12)離した |
4. 最後に
今回は、SteamVR Unity Plugin v1でのコントローラの情報の取得方法を紹介しました。完全に機能を網羅できているとは思えていませんが、これでコントローラの情報を使えるようになります。
SteamVR Unity Plugin v1 はもうアップデートされていません。現在の SteamVR の発展と、SteamVR Unity Plugin v2 の発展に期待をしつつ、段々と SteamVR Unity Plugin v2 の SteamVR Interaction System などの使用方法へとこの記事もアップデートしていくことを期待しています。
今回紹介した方法以外で何か良い方法、間違っていることなどがあったら、コメントをいただけると幸いです。