先にまとめ
VMTで両手の姿勢をUnity上で取得する方法です。以下のスクリプトを任意のGameObjectにアタッチし、Inspectorで、姿勢確認用のTransform変数に任意のオブジェクトを代入し、左右のコントローラのシリアル番号を代入します。シリアル番号は、VMT ManagerのDevice画面で確認できます。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(uOSC.uOscClient))] [RequireComponent(typeof(uOSC.uOscServer))] public class WatchHands : MonoBehaviour { uOSC.uOscClient client; uOSC.uOscServer server; [SerializeField] Transform rightHand; [SerializeField] Transform leftHand; [SerializeField] string rightHandSerial = ""; [SerializeField] string leftHandSerial = ""; void Start() { client = GetComponent<uOSC.uOscClient>(); server = GetComponent<uOSC.uOscServer>(); //アドレス固定 client.address = "127.0.0.1"; client.port = 39570; server.port = 39571; //イベント追加 server.onDataReceived.AddListener(OnDataReceived); server.onServerStopped.AddListener(OnServerStopped); //購読開始 Subscribe(); } public void OnDataReceived(uOSC.Message message) { if (message.address == "/VMT/Out/SubscribedDevice") { //受け取った情報がどのデバイスのものか、シリアル番号を取得 string serial = (string)message.values[0]; //シリアル番号を識別 //受け取った姿勢情報を反映させるTransformをtargetに代入 //どちらのコントローラでもなければreturn Transform target = null; if (serial == rightHandSerial) target = rightHand; else if (serial == leftHandSerial) target = leftHand; else return; //反映 float x = (float)message.values[1]; float y = (float)message.values[2]; float z = (float)message.values[3]; float qx = (float)message.values[4]; float qy = (float)message.values[5]; float qz = (float)message.values[6]; float qw = (float)message.values[7]; target.position = new Vector3(x, y, -z); target.rotation = new Quaternion(qx, qy, -qz, -qw); } } public void OnServerStopped(int x) { Unsubscribe(); } private void OnDestroy() { Unsubscribe(); } void Unsubscribe() { Debug.Log($"Unsubscribed:{rightHandSerial}"); client.Send("/VMT/Unsubscribe/Device", rightHandSerial); Debug.Log($"Unsubscribed:{leftHandSerial}"); client.Send("/VMT/Unsubscribe/Device", leftHandSerial); } void Subscribe() { Debug.Log($"Subscribed:{rightHandSerial}"); client.Send("/VMT/Subscribe/Device", rightHandSerial); Debug.Log($"Subscribed:{leftHandSerial}"); client.Send("/VMT/Subscribe/Device", leftHandSerial); } }
詳しく説明
SteamVRのドライバ(OpenVR)とのデータのやり取りを簡単に行える、Virtual Motion Tracker(VMT)というアプリケーションがあります。
VMTを使う中で、HMD位置の取得は、以下に示す公式Sampleの、WatchHMD.csを使えば簡単にできます。 github.com
しかし、コントローラなどのHMD以外の位置取得で、デバイスのシリアル番号の確認に少し迷ったので、メモとして残しておきます。
WatchHMD.csの21行目では、デバイス情報の購読を要求しています。
client.Send("/VMT/Subscribe/Device", "HMD");
つまり、デバイス情報を定期的に送信してくれるように、OpenVRへ(VMT経由で)要求しています。この時の第2引数で、購読したいデバイスを指定します。
上記のように、HMDの場合は、"HMD"という文字列を渡せばよいです。しかし、HMD以外の、コントローラやトラッカーの場合は、各デバイスのシリアル番号を渡してやる必要があります。
このシリアル番号は、VMT Manager上で確認できます。VMT ManagerのDeviceタブを開き、Reloadを押すと、現在使用しているデバイスの一覧が出ます。このリストから、取得したいデバイスを選択し、Copy to Clipboardでシリアルを取得できます。
本記事の最初に示したコードを再掲します。これをUnity上で使用し、Inspectorに、コピーしたシリアル番号を張り付ければ、SteamVR内でのコントローラ位置が、Unity上のオブジェクトに反映されるはずです。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(uOSC.uOscClient))] [RequireComponent(typeof(uOSC.uOscServer))] public class WatchHands : MonoBehaviour { uOSC.uOscClient client; uOSC.uOscServer server; [SerializeField] Transform rightHand; [SerializeField] Transform leftHand; [SerializeField] string rightHandSerial = ""; [SerializeField] string leftHandSerial = ""; void Start() { client = GetComponent<uOSC.uOscClient>(); server = GetComponent<uOSC.uOscServer>(); //アドレス固定 client.address = "127.0.0.1"; client.port = 39570; server.port = 39571; //イベント追加 server.onDataReceived.AddListener(OnDataReceived); server.onServerStopped.AddListener(OnServerStopped); //購読開始 Subscribe(); } public void OnDataReceived(uOSC.Message message) { if (message.address == "/VMT/Out/SubscribedDevice") { //受け取った情報がどのデバイスのものか、シリアル番号を取得 string serial = (string)message.values[0]; //シリアル番号を識別 //受け取った姿勢情報を反映させるTransformをtargetに代入 //どちらのコントローラでもなければreturn Transform target = null; if (serial == rightHandSerial) target = rightHand; else if (serial == leftHandSerial) target = leftHand; else return; //反映 float x = (float)message.values[1]; float y = (float)message.values[2]; float z = (float)message.values[3]; float qx = (float)message.values[4]; float qy = (float)message.values[5]; float qz = (float)message.values[6]; float qw = (float)message.values[7]; target.position = new Vector3(x, y, -z); target.rotation = new Quaternion(qx, qy, -qz, -qw); } } public void OnServerStopped(int x) { Unsubscribe(); } private void OnDestroy() { Unsubscribe(); } void Unsubscribe() { Debug.Log($"Unsubscribed:{rightHandSerial}"); client.Send("/VMT/Unsubscribe/Device", rightHandSerial); Debug.Log($"Unsubscribed:{leftHandSerial}"); client.Send("/VMT/Unsubscribe/Device", leftHandSerial); } void Subscribe() { Debug.Log($"Subscribed:{rightHandSerial}"); client.Send("/VMT/Subscribe/Device", rightHandSerial); Debug.Log($"Subscribed:{leftHandSerial}"); client.Send("/VMT/Subscribe/Device", leftHandSerial); } }