😎

Xreal Lightで手のひらを見た時にメニュー画面を出したい

2024/06/06に公開

環境

  • unity 2021.3.33f
  • NRSDK 2.1.0
  • MacOS sonoma

前提

  • NRSDKをunityに導入し終わっていること

背景

Xreal lightでアプリ作る時に、Hololens2とかQuest3みたいに特定ハンドジェスチャーをしたらメニュー画面を表示したいな〜と思ったのがきっかけです。
今回は左手の手のひらを見たときに好きなオブジェクトを表示するようにします。

実装

以下のような感じになりました。

  • 今回表示するgameobjectは"_menu"変数に入れ込みました
  • 工夫点としては、
    • 手のひらの角度にある程度余裕を持たせた点("_thresholdAngl"のこと)
    • gameobjectを手のひらに表示する時、手のひらのちょっと上に表示されるように手のひら座標系にオフセット値を持たせた点("palmPose.rotation * new Vector3(0, 0, 0.1f)"のとこ)

です。

using UnityEngine;

namespace NRKernal.NRExamples
{
    public class DisplayMenuToPalm : MonoBehaviour
    {
        //手のひらに表示したいオブジェクト(menu)
        [SerializeField] private GameObject _menu;
        //視界の方向(グラスの方向)を検出するためのオブジェクト
        [SerializeField] private GameObject _nRCameraRig;
        //menuを表示する際の手のひらの角度の閾値
        [SerializeField] private float _thresholdAngle = 30f;

        void Update()
        {
            // 手のトラッキングが実行中かどうかを確認
            if (!NRInput.Hands.IsRunning)
            {
                return;
            }

            // 左手の状態を取得
            HandState handState = NRInput.Hands.GetHandState(HandEnum.LeftHand);
            // 左手がトラッキングされているか or 手が開いている状態かどうかチェック
            if (!handState.isTracked || handState.currentGesture != HandGesture.OpenHand)
            {
                // 条件に一致したらmenuを表示しない
                _menu.SetActive(false);
                return;
            }

            // 手のひらの座標、回転角情報を取得
            Pose palmPose = handState.GetJointPose(HandJointID.Palm);
            // 手のひらがxreal lightのグラスの方向を向いていれば、手のひらの位置座標と回転方向に合わせてmenuオブジェクトを表示する
            if (IsPalmFacingCamera(palmPose))
            {
                // 手のひらの上に表示されるようにmenuオブジェクトのオフセット値を設定(0.1f)
                Vector3 offset = palmPose.rotation * new Vector3(0, 0, 0.1f);
                Vector3 newPosition = palmPose.position + offset;
                _menu.transform.position = newPosition;
                _menu.transform.rotation = palmPose.rotation;
                _menu.SetActive(true);
            }
            else
            {
                _menu.SetActive(false);
            }
        }

        private bool IsPalmFacingCamera(Pose palmPose)
        {
            // 手のひらの法線を取得(palmの手のひら方向はVector3.backであることに注意!)
            Vector3 palmNormal = palmPose.rotation * Vector3.back;
            // カメラの前方向ベクトルを取得
            Vector3 cameraForward = _nRCameraRig.transform.forward;
            // 二つのベクトル間の角度を計算
            float angle = Vector3.Angle(palmNormal, cameraForward);

            // 角度が閾値以下であれば、手のひらがカメラを向いていると見なしてtrueを返す
            return angle <= _thresholdAngle;
        }
    }
}

まとめ

残念ながら画面録画やスクショ機能は実装できていないのでお見せできませんが、実機で動かして手のひらに好きなgameobjectを表示できるようになりました。次はメニュー画面とハンドメッシュのインタラクションあたりを記事化できればと思います。

Discussion