先にまとめ
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 = 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)というアプリケーションがあります。
gpsnmeajp.github.io
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 = 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);
}
}