【UnityでOculus Quest向けのアプリを作る】ハンドトラッキングで物を掴む


◆ はじめに

前回、ハンドトラッキングの実装しました。
なので、今回は物を掴む方法について書きます。

手の動き、指の取得は

OculusQuest ハンドトラッキングSDKから、指Boneの情報を取得し分析する

こちらの記事を参考にしてください。

必要あれば前の記事を参考してください。
Oculus Quest向けのAPPをビルドして、動作確認する
物を掴む為には(物体の制作)
ハンドトラッキングの実装

今回の記事ですが、
完全に自己流の方法でやっているので、無理矢理感がある為、要注意です。

◆ 開発環境

macOS Mojave バージョン 10.14.6
Unity 2018.4.12f1
Android SDK
ハンドトラッキング機能を使用する為、
Oculusのバージョンは(Ver12)以上、にアップデートする必要があります。

◆ 手順

  • 準備した物の説明
  • 掴む実装方法説明
  • 指動作の判断
  • 論理およびコード説明]
  • 動作確認

準備した物の説明


まず、必要なObjectを準備しました。
ハンドトラッキングを実装して、物体(Cube)とテーブルを用意します。
Cubeのサイズは自由に設置しても構いません。

プログラムがちゃんと動いてるかどうか、
結果を見やすくする為に、Canvasを作りました。必須ではないですが、あった方が便利です。
また、テーブルも必須ではないです。

掴む実装方法説明

以前の物を掴む為にはでは、
無事に手を表示後普通に物体を掴む事ができますが、ハンドトラッキングではできないらしいです。

なので、
今回やるべき事は手が物体を当たった状態で、掴むという動作をした時、物体を手の階層に入れる。
要するに、物体を手の子要素にするという事です。

勿論、
子要素にするのではなく、掴む判定の時、物体のポジションを手と同じにするというのも方法の一つです。

指動作の判断

ovrHand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ovrHand : MonoBehaviour
{
    private OVRHand _ovrHand;
    // 掴む状態
    public bool _catching;

    // Start is called before the first frame update
    void Start()
    {
        _catching = false;
        _ovrHand = this.gameObject.GetComponent<OVRHand>();
    }
    // Update is called once per frame
    void Update()
    {
        if (_ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Index))
        {
            _catching = true;
        }
        else
        {
            _catching = false;
        }
    }
}

上記のコードは「OVRHandPrefab」に追加します。
手を取得し、変数「_ catching」を作ります。「_ catching」の説明は後で

GetFingerIsPinchingは、指先と指先が触れているかどうかを取得する事ができます。
OVRHand.HandFingerは、親指とどの指が触れているかを判定できます。今回は人差し指にします。

今回は、指先と指先が触れてたら「_catching」は真にします。
状態はCanvas上に表示します。
(Canvas作ってないなら書く必要はないです、この後にCanvas関する説明も全部省略します)

論理およびコード説明

cubeTouch.cs
    private ovrHand _ovrHand;
    private bool _touchIN;      //当たり
    private bool _catch;        //掴む
    private bool _hold;         //取れる

    // Start is called before the first frame update
    void Start()
    {
        _ovrHand = FindObjectOfType<ovrHand>();
    }
void Update()
    {
        // 手は選択状態なのかを常に取得する
        _catch = _ovrHand._catching;
        touchCube();
        CatchCube();
    }
void touchCube()
    {
        if (_touchIN == true && _catch == false)
        {
            _hold = true;
        }
    }

    void CatchCube()
    {
        if (_hold == true && _catch == true)
        {
            _touchIN = false;
        }
        else if (_touchIN = false && _touchIN == false)
        {
            _hold = false;
        }
    }

まず、掴むという動作について考えます。

手は握った状態で物体に当たったら、物体をつかめる。
物体に当たった後、手を握ると、物体をつかめる。

このことを、上記のコードで表しています。

cubeTouch.cs
void OnCollisionStay(Collision other)
    {
        // 物体をタッチした時 タッチ変数は 真になる
        if (other.gameObject.name == "Hand_Index3_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Index2_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Thumb3_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Thumb2_CapsuleRigidBody" 
            )
        {
            _touchIN = true;
            // 位置固定し物理削除する
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            gameObject.GetComponent<Rigidbody>().isKinematic = true;
        }
    }

上の説明に合わせ、
まず、物体をタッチしないと始まらないので、当たり判定書きます。

それぞれの指の名前は一番上の参考リンクを参照してください。
タッチしたら、「 _touchIN」は真になります。

cubeTouch.cs
void CatchCube()
    {
        if (_hold == true && _catch == true)
        {
            Debug.Log("_catch = " + _catch + " 物を掴みました!");
            // 右手の子供になる
            gameObject.transform.SetParent(_rightHandAnchor.gameObject.transform);
            // 重力抜き 当たり判定しない
            gameObject.GetComponent<Rigidbody>().useGravity = false;
            gameObject.GetComponent<BoxCollider>().isTrigger = true;
            _touchIN = false;
        }
        else if (_touchIN = false && _touchIN == false)
        {
            // 親関係解除
            gameObject.transform.parent = null;
            // 重力 当たり判定 復帰
            gameObject.GetComponent<Rigidbody>().useGravity = true;
            gameObject.GetComponent<BoxCollider>().isTrigger = false;
            // 位置移動可 物理復帰
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
            gameObject.GetComponent<Rigidbody>().isKinematic = false;
            _hold = false;
        }
    }

今回のCubeは物理演算を入れているので、掴む時は物理演算を抜く必要があります。
「CatchCube()」の中に必要な処理を追加します。
コードの意味は 、コメントアウトで書いているので参考にしてください。

最後に、「cubeTouch.cs」をCubeの中に入れば完成です。

動作確認

ビルドして、動作確認するとこうなります。