しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

HoloLens で Unity の Button へのホールドを検出してみる

※ これは 2016/05/31 Unity HoloLens Technical Preview ver.5.4.0beta14(Windows版) 時点の情報です

最新版では動作が異なる可能性がありますのでご注意ください

前々回 は Button に視線が当たった場合にカーソルのようなものを表示するようにしてみました

今度は下記のチュートリアルを参考に、ホールドジェスチャ(実機の場合人差し指を折る動作、エミュレータの場合右マウスボタン押下)でも状態遷移するようにしてみようと思います

Holograms 101E - Chapter 3 Gestures

youtu.be

HoloLens のジェスチャ検出は UnityEngine.VR.WSA.Input.GestureRecognizer という専用のクラスが用意されているようなのでこちらを使用して VRUI を修正

using UnityEngine;
using System.Collections;
using System;
using UnityEngine.UI;
using UnityEngine.VR.WSA.Input;

public class VRUI : MonoBehaviour
{
    /// <summary>
    /// ボタンのラベルテキスト
    /// </summary>
    [SerializeField]
    private UnityEngine.UI.Text buttonLabel;

    /// <summary>
    /// ターゲッテイング用カーソル
    /// </summary>
    [SerializeField]
    private GameObject cursor;

    /// <summary>
    /// ジェスチャ検知
    /// </summary>
    private GestureRecognizer gesture;

    /// <summary>
    /// 視線オーバーフラグ
    /// </summary>
    private bool isOver = false;

    /// <summary>
    /// ホールドジェスチャフラグ
    /// </summary>
    private bool isHold = false;

    /// <summary>
    /// 初期処理
    /// </summary>
    public void Awake()
    {
        this.gesture = new GestureRecognizer();
        this.gesture.SetRecognizableGestures(GestureSettings.Hold);
        this.gesture.HoldStartedEvent += this.OnButtonHold;
        this.gesture.HoldCompletedEvent += this.OnButtonHoldEnd;
        this.gesture.HoldCanceledEvent += this.OnButtonHoldEnd;
        this.gesture.StartCapturingGestures();
    }

    /// <summary>
    /// 破棄処理
    /// </summary>
    public void OnDestroy()
    {
        if (this.gesture != null && this.gesture.IsCapturingGestures())
        {
            this.gesture.StopCapturingGestures();
            this.gesture.HoldStartedEvent -= this.OnButtonHold;
            this.gesture.HoldCompletedEvent -= this.OnButtonHoldEnd;
            this.gesture.HoldCanceledEvent -= this.OnButtonHoldEnd;
        }
    }

    /// <summary>
    /// 定期的な処理
    /// </summary>
    public void FixedUpdate()
    {
        RaycastHit hit;
        if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit))
        {
            this.cursor.transform.position = hit.point;
            this.cursor.transform.rotation = Quaternion.Euler(hit.normal);
            this.cursor.SetActive(true);

            var target = hit.collider.gameObject.GetComponent<Button>();
            if (target != null)
            {
                this.isOver = true;
            }
        }
        else
        {
            this.isOver = false;
            this.cursor.SetActive(false);
        }
    }

    /// <summary>
    /// 毎フレームごとの処理
    /// </summary>
    public void Update()
    {
        if (this.isOver)
        {
            if (this.isHold)
            {
                this.buttonLabel.text = "Pressed";
                return;
            }

            this.buttonLabel.text = "Over";
            return;
        }

        if (!this.isHold)
        {
            this.buttonLabel.text = "Button";
        }
        //ホールド中に視線を外した場合は何もしない
    }

    /// <summary>
    /// 視線ホールドイベントハンドラ
    /// </summary>
    /// <param name="source">入力ソース</param>
    /// <param name="headRay">頭部の方向</param>
    private void OnButtonHold(InteractionSourceKind source, Ray headRay)
    {
        this.isHold = true;
    }

    /// <summary>
    /// 視線ホールド終了イベントハンドラ
    /// </summary>
    /// <param name="source">入力ソース</param>
    /// <param name="headRay">頭部の方向</param>
    private void OnButtonHoldEnd(InteractionSourceKind source, Ray headRay)
    {
        this.isHold = false;
    }
}

Awake で GestureRecognizer の利用準備をして、OnDestroy で後始末をしています

物理演算処理用の FixedUpdate() では Button と視線の衝突判定のみ行い、画面描画に適した Update() で Button の表示遷移をしました

さてこれでお試し!

f:id:matatabi_ux:20160702122039g:plain

一応ホールドも取れているようですが、エミュレーター上だと右マウス押下から画面への反映がワンテンポ遅れるような?

実機でも遅いんだろうか・・・まぁいずれにせよ実機でのジェスチャ認識はそれほど高速に検出できないとは思いますが