今回は UnityEngine.XR の InputDevices クラスを使って、HoloLens 2 の Tap を検出してみたいと思います。
検証環境
- Unity 2020.3
- MRTK v2.8.3
UnityEngine.XR InputDevices を使って HoloLens 2 のタップを検出する
InputDevices
クラスは、XR入力サブシステム (XR Input Subsystem) 内のデバイスにアクセスするためのインターフェースです。InputDevices
クラスでは、以下3つの方法で入力デバイスへアクセスが可能です。
- デバイス特性 (Characteristics)
- ロール (Role)
- XRノード (XRNode)
今回は XRNode を用いた入力デバイスへのアクセスを行います。XRNode を用いた入力デバイスへのアクセスは InputDevices.GetDeviceAtXRNode
メソッドを使用します。
public static XR.InputDevice GetDeviceAtXRNode (XR.XRNode node);
XRNode は 列挙型 (enum) で定義されています。
XRNode | 説明 |
---|---|
CenterEye | ユーザーの目の瞳孔の中間地点 |
GameController | コンソールスタイルのゲームコントローラーアプリケーションは複数のゲームコントローラーデバイスを持つことができます。 |
HardwareTracker | ハードウェアトラッキングデバイス。通常、ユーザーや物理的なアイテムに取り付けられます。複数のハードウェアトラッカーノードが可能です。 |
Head | XRシステムによって計算された、ユーザーの頭の中心点 |
LeftEye | ユーザーの左目 |
LeftHand | ユーザーの左手 |
RightEye | ユーザーの右目 |
RightHand | ユーザーの右手 |
TrackingReference | Oculusカメラなどのトラッキング基準点。複数のトラッキング参照ノードが存在できます。 |
// 右手のインスタンスを取得する場合
InputDevice device = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
// 左手のインスタンスを取得する場合
InputDevice device = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
デバイスが取得出来たら、次にデバイスの状態を取得します。デバイスの状態を取得する際は、InputDevice.TryGetFeatureValue
メソッドを使用します。TryGetFeatureValue()
は、機能の現在の値へのアクセスを試行して、以下の結果を返却します。
- 引数で指定された機能の値を正常に取得した場合は
true
- 現在のデバイスが指定された機能をサポートしていない場合、または、デバイスが無効な場合は
false
また、デバイスの機能の指定は、CommonUsage
クラスを用いて行います。調査をしていると、HoloLens 2 の タップ は CommonUsage.primaryButton
にマッピングされているみたいです。タップ状態の有無は isTapping
変数に格納します。isTapping
が true
の場合はタップしている状態、false
の場合はタップしていない状態を表します。
device.TryGetFeatureValue(CommonUsages.primaryButton, out bool isTapping);
これらを組み合わせて、(ショート)タップを検出するコードを作成することができます。
using UnityEngine;
using UnityEngine.XR;
public class Sample : MonoBehaviour
{
/// <summary>
/// タップ時間を記録するタップタイマー (右手/左手)
/// </summary>
private float[] _tappingTimer = { 0, 0 };
void Update()
{
for (int i = 0; i < 2; i++)
{
// 右手/左手のデバイスインスタンスを取得する
InputDevice device = InputDevices.GetDeviceAtXRNode((i == 0) ? XRNode.RightHand : XRNode.LeftHand);
// PrimaryButton の現在の状況を取得して、tapping に格納する
if (device.TryGetFeatureValue(CommonUsages.primaryButton, out bool isTapping))
{
// タップをしていない場合
if (!isTapping)
{
// タップが1秒以内であるかどうかを判定
if (0f < _tappingTimer[i] && _tappingTimer[i] < 1f)
{
// タップを検出
if (device.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 handPosition))
{
// タップされた位置をログ出力
Debug.Log(handPosition);
}
}
// タップタイマーのリセット
_tappingTimer[i] = 0;
}
// タップをしている場合
else
{
// タップタイマーを更新
_tappingTimer[i] += Time.deltaTime;
}
}
}
}
}
以下 GitHub に MS 公式のサンプルコードが用意されているので、興味のある方はこちらを試されるのが良いと思います。
Refs