読者です 読者をやめる 読者になる 読者になる

しっぽを追いかけて

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

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

HoloLens 用に Navigate ジェスチャで 3D オブジェクトを配置する Unity アプリを作る

AR C# HoloLens Unity VR Windows 10 Windows ランタイムアプリ

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

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

前回は回り込んでくるねこの動きを作りましたが、今度は下記の動画にも出てくるようなドラッグ的な動作(Manipuration または Navigation)を試す実装を作ってみたいと思います

今回は Navigation(相対的な座標移動)ジェスチャを検知して 3D ボールを落とす動作を作ります

HoloLens のジェスチャは UnityEngine.VR.WSA.Input.GestureRecognizer を利用すれば簡単に検知できます

GestureRecognizer.StartCapturingGestures() メソッドを呼び出すことでジェスチャを検知できるようになり、NavigationStartedEvent でドラッグの開始、NavigationUpdatedEvent でドラッグ中、NavigationCompletedEvent でドラッグ完了、NavigationCanceledEvent でキャンセル時のイベントハンドラを指定できるようです

この API を利用して書いてみたのが下記のコード

using UnityEngine;
using UnityEngine.VR.WSA.Input;

/// <summary>
/// ボール生成ビヘイビア
/// </summary>
public class BallCreater : MonoBehaviour
{
    /// <summary>
    /// HoloLens Gesture Recognizer
    /// </summary>
    private GestureRecognizer recognizer;

    /// <summary>
    /// ボールのモデル
    /// </summary>
    [SerializeField]
    private GameObject ballPrefab;

    /// <summary>
    /// ボールを追いかけるネコ
    /// </summary>
    [SerializeField]
    private BallChaser chaser;

    /// <summary>
    /// ボールのインスタンス
    /// </summary>
    private GameObject ball;

    /// <summary>
    /// カメラ
    /// </summary>
    private Camera mainCamera;

    /// <summary>
    /// Navigation 中フラグ
    /// </summary>
    private bool navigationStarting = false;

    /// <summary>
    /// 初期処理
    /// </summary>
    public void Awake()
    {
        this.mainCamera = Camera.main;
        this.recognizer = new GestureRecognizer();
        this.recognizer.NavigationStartedEvent += this.OnNavigationStarted;
        this.recognizer.StartCapturingGestures();
        this.chaser.Ball = null;
    }

    /// <summary>
    /// Navigation ジェスチャの開始
    /// </summary>
    /// <param name="source">入力種別</param>
    /// <param name="normalizedOffset">標準化座標移動量</param>
    /// <param name="headRay">頭の向き</param>
    private void OnNavigationStarted(InteractionSourceKind source, Vector3 normalizedOffset, Ray headRay)
    {
        this.navigationStarting = false;
        this.chaser.Ball = null;

        this.recognizer.NavigationUpdatedEvent += this.OnNavigationUpdated;
        this.recognizer.NavigationCompletedEvent += this.OnNavigationCompleted;

        if (this.ball != null)
        {
            Destroy(this.ball);
            this.ball = null;
        }

        this.ball = Instantiate<GameObject>(this.ballPrefab);
        this.MoveBall(normalizedOffset);
    }

    /// <summary>
    /// Navigation ジェスチャの移動
    /// </summary>
    /// <param name="source">入力種別</param>
    /// <param name="normalizedOffset">標準化座標移動量</param>
    /// <param name="headRay">頭の向き</param>
    private void OnNavigationUpdated(InteractionSourceKind source, Vector3 normalizedOffset, Ray headRay)
    {
        this.navigationStarting = true;

        this.MoveBall(normalizedOffset);
    }

    /// <summary>
    /// Navigation ジェスチャの完了
    /// </summary>
    /// <param name="source">入力種別</param>
    /// <param name="normalizedOffset">標準化座標移動量</param>
    /// <param name="headRay">頭の向き</param>
    private void OnNavigationCompleted(InteractionSourceKind source, Vector3 normalizedOffset, Ray headRay)
    {
        this.navigationStarting = false;

        this.recognizer.NavigationUpdatedEvent -= this.OnNavigationUpdated;
        this.recognizer.NavigationCompletedEvent -= this.OnNavigationCompleted;

        this.MoveBall(normalizedOffset);
        this.ReleaseBall();
    }

    /// <summary>
    /// ボールの移動
    /// </summary>
    /// <param name="normalizedOffset">標準化座標移動量</param>
    private void MoveBall(Vector3 normalizedOffset)
    {
        this.ball.transform.position = this.mainCamera.transform.position + this.mainCamera.transform.forward * 1.8f + normalizedOffset / 4f;
        if (this.ball.transform.position.y < 0.3f)
        {
            this.ball.transform.position = new Vector3(this.ball.transform.position.x, 0.3f, this.ball.transform.position.z);
        }
    }

    /// <summary>
    /// ボールを落とす
    /// </summary>
    private void ReleaseBall()
    {
        this.ball.GetComponent<Rigidbody>().useGravity = true;
        this.chaser.Ball = this.ball;
    }
}

BallChaser は落としたボールを追いかけるような動作を記述したコードで今回は割愛

Navigation のジェスチャでボールをドラッグ移動させ、リリースして Navigation を完了した際にボールを落としてねこ(きじとら)に追いかけさせるようにしてみました

f:id:matatabi_ux:20160503053745g:plain

実機がないためジェスチャの移動量調整は適当なんですが、すんなりドラッグ(エミュレーター上ではマウスの右ボタンドラッグ)によるボール落下位置の指定はできました