オリジナルのハンドジェスチャーを実装する

こんにちは。HoloLensチームの老田です!

今回はHoloLens2でオリジナルのハンドジェスチャーの実装についてです。

そもそもどんなハンドジェスチャーがあるの?

  • AirTap
  • ホーム画面を出すジェスチャー (手首に人差し指をあてる)
  • ホーム画面を出すジェスチャー (手首に視線を合わせて人差し指と親指を合わせる)
    詳しくは下記を参照してください。

https://docs.microsoft.com/ja-jp/hololens/hololens2-basic-usage

https://docs.microsoft.com/ja-jp/windows/mixed-reality/system-gesture

オリジナルのジェスチャーを実装することができるのか?

かなり泥臭い方法になってしまいますが、実装することは可能なようでした。

今回は以下のようなジェスチャーを認識するように実装していきたいと思います。

実装のしかた

考え方としては指一つ一つを手のひらに対して同じ向きかどうか、そうでないかを判断することで指を曲げているかどうかを認識することができます。

using UnityEngine;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;

public class GestureTest : MonoBehaviour
{

    [SerializeField, Range(0.0f, 90.0f)] private float flatHandThreshold = 45.0f;
    [SerializeField, Range(0.0f, 90.0f)] private float facingThreshold = 80.0f;
    [SerializeField] GameObject sphere = null;
    [SerializeField] TextMesh textMesh = null;


    private void Update()
    {

        if (CheckHandSgin())
        {
            sphere.SetActive(true);
        }
        else
        {
            sphere.SetActive(false);
        }

    }

    private bool CheckHandSgin()
    {
        // 右手のみ判定したいので右手を取得
        var jointedHand = HandJointUtils.FindHand(Handedness.Right);

        // 手のひらが認識できているか
        if (jointedHand.TryGetJoint(TrackedHandJoint.Palm, out MixedRealityPose palmPose))
        {
            MixedRealityPose thumbTipPose, indexTipPose, middleTipPose, ringTipPose, pinkyTipPose;

            if (jointedHand.TryGetJoint(TrackedHandJoint.IndexTip, out indexTipPose) && jointedHand.TryGetJoint(TrackedHandJoint.PinkyTip, out pinkyTipPose))
            {
                var handNormal = Vector3.Cross(indexTipPose.Position - palmPose.Position, pinkyTipPose.Position - indexTipPose.Position).normalized;
                handNormal *= (jointedHand.ControllerHandedness == Handedness.Right) ? 1.0f : -1.0f;
                if (Vector3.Angle(palmPose.Up, handNormal) > flatHandThreshold)
                {
                    return false;
                }
            }

            // 中指と薬指の情報を取得
            if (jointedHand.TryGetJoint(TrackedHandJoint.MiddleTip, out middleTipPose) && jointedHand.TryGetJoint(TrackedHandJoint.RingTip, out ringTipPose))
            {
                var handNormal = Vector3.Cross(middleTipPose.Position - palmPose.Position, ringTipPose.Position - indexTipPose.Position).normalized;
                handNormal *= (jointedHand.ControllerHandedness == Handedness.Right) ? 1.0f : -1.0f;
                if (Vector3.Angle(palmPose.Up, handNormal) < flatHandThreshold)
                {
                    return false;
                }
            }
        }
        // 手のひらを向けているか確認
        return Vector3.Angle(palmPose.Up, CameraCache.Main.transform.forward) < facingThreshold;
    }

}

適当なオブジェクトにアタッチしてsphereにオブジェクトを割り当ててあげれば以下のGIFように動作すると思います。

最後に

注意点としてUnityエディタ上ではデバッグできませんのでそこそこつらいです。

今回は右手だけ反応するように実装しましたが、どちらかの手や左手だけ反応するようにも実装できます。

MagicLeapOneでEyeTracking

こんにちは。HoloLensチームの老田です!

前回の記事「MagicLeapOneで2つのコントローラを使う方法」に引き続き、EyeTrackingができるようなので検証してみました。

なにができるの?

  • 両目での視線のトラッキング
  • 片目の視線のトラッキング
  • 視線のステータスの取得
  • 瞬きの検出(左右別々に判定可能)

視線のトラッキングについて

Holo2のような視線のトラッキングが可能になっています。
前提としてかぶり方次第ではずれているように感じますので頑張っていい位置を探してください。

Holo2と比べるとそこまで正確にトラッキングできているわけではなさそうでした。
誤差や精度についての話はここでは割愛します。

ある程度ずれていても両目でトラッキングしている場合はそれなりに動いてくれます。
片目の場合はしっかりかぶっていないとカーソルが視界にすら入ることもままなりません。

視線のステータスの取得

MLEyes.CalibrationStatusから以下が取得できます。

  • MLEyeTrackingCalibrationStatus_None(未検出の状態)
  • MLEyeTrackingCalibrationStatus_Bad(1度検出した後に検出できなくなった状態)
  • MLEyeTrackingCalibrationStatus_Good(検出できている状態)

ちなみに瞬きをした場合はMLEyeTrackingCalibrationStatus_Badと判定されます。
また、瞬きだけではなくMLを外した時にも上記と同様の扱いになります。

瞬きについて

以下のbool型のフィールドで取得できます。
MLEyes.LeftEye.IsBlinking
MLEyes.RightEye.IsBlinking

精度はよいです。
普通の一瞬だけの瞬きでも取得でき、ウィンクでも行けます。
ただし、ウィンクしなれてないのでウィンクだけ難しく感じました。

サクッと動かす(両目の視線のトラッキング)

特にあれこれする必要もなく上記のInspectorの設定に合わせるだけで動作します。
MaterialやSharder、Scriptに関してはMagicLeapのExamplesを入れていればすべてそろっています。

実行すると視線の先にカーソルが表示されるようになります。

ざっくりと解説するとWorldRaycastEyes内でUpdateごとにOnRaycastHitが呼ばれています。
ここでオブジェクトのTransformを変更しています。

もう少し深掘りしてみる(ウィンクで何か操作する)

右ウィンクしたときに0.5秒たったら何かするものを実装してみました。
あとは豆腐の色変えたりお好きに使ってください。

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.MagicLeap;

public class EyeTracking : MonoBehaviour
{
  bool isBlink = false;
  float count = 0f;
  [SerializeField] float threshold = 0.5f;

  private void Start()
  {
    StartCoroutine(BlinkCheck());
  }
  private IEnumerator BlinkCheck()
  {
    MLEyes.Start();
    while (true)
    {
      // 右目のウィンクだけを判定
      if (!isBlink && MLEyes.RightEye.IsBlinking && !MLEyes.LeftEye.IsBlinking)
      {
        count += Time.deltaTime;
        // 右ウィンクしている状態で0.5秒たったらコマンドと認識
        if (count > threshold)
        {
          isBlink = true;
          Debug.Log("is Blink Command");
        }
      }
    }

    // 両目を開いたらリセット
    if (!MLEyes.RightEye.IsBlinking && !MLEyes.LeftEye.IsBlinking)
    {
      count = 0f;
      isBlink = false;
    }
    yield return null;
    }
  }
}

 

MagicLeapOneで2つのコントローラを使う方法

こんにちは。HoloLensチームの老田です。

前回の記事「MagicLeapOne向けアプリ開発の始めかた」に引き続き、コントローラ2台の接続方法とサンプルの実装まで行いたいと思います。

接続方法

1. 1台目はつながれていることを確認

2. 2台目をケーブルでつなぐ

3. 設定のConectivityのControlから接続されていることを確認する

4. 2台目のケーブルを抜く

画像のように1台目がprimary、2台目がsecondaryといった表現で接続されていることがわかります。

ホームでの挙動

1台目(primary)は通常通り動作します。

2台目(secondary)はホームボタンのみ動作します。

自作アプリでコントローラを2台の入力を受け取る

ControllerというPrefabがありますのでこれをScene上に2つ配置します。

ControllerConnectionHandlerというスクリプトがありますのでその中のDeviceTypesAllowedをそれぞれLeftとRightに変更してください。

あとはビルドしてデプロイして動かすとこんな感じになります。

MagicLeapOne始めました!

こんにちは。HoloLensチームの老田です。

日頃からHoloLens開発を行っておりますがこの度弊社にもMagicLeapOneがやってきました!

HoloLensとの大きな違いはコントローラが付いていること、本体とHMDがケーブルでつながれているところです。

性能面などの違いが知りたい方やMagicLeapOneが何かをご存知ない方はこちらをご覧ください。

引き続きHoloLens開発も行っては行きますがMagicLeapOneの検証や開発も行っていきたいと思います!

また後日MagicLeapOneの記事をアップ予定ですのでお楽しみに!