OculusQuestでハンドトラッキングをしてみよう!<後編> 〜オブジェクトをつまむ〜

前回の記事ではOculusQuestでハンドトラッキングを適用して手を表示できるところまでできました。
今回はハンドトラッキングを利用してバーチャル世界のオブジェクトを手でつまめるようににしていきたいと思います。

筆者環境

  • MacBookPro(2017年モデル)
  • Unity バージョン2019.2.17f1

シリーズ

  1. 【図解】OculusQuest開発環境を整える導入編(2019/12月版)
  2. OculusQuestでハンドトラッキングをしてみよう!<前編>

前提条件

1. ハンドトラッキング用プレハブの解釈

前回記事でハンドトラッキングをプロジェクトに適用して、手が表示できるところまでできました。
まずはUnityプロジェクトのヒエラルキーが以下の図のようになっているのを確認します。

前章までの作業

確認できたらOVRCameraRig > TrackingSpace > RightHandAnchor > OVRHandPrefabを選択してインスペクタを確認します。

OVRHandPrefabのインスペクタ

インスペクタをみるといくつかのスクリプトがアタッチされています。
ざっくり解釈すると以下のようになります。

  • OVRHand: トラッキングされた手の状態・描画等を管理
  • OVRSkeleton: トラッキングされた手の骨組みの状態を管理
  • OVRSkeletonRenderer: トラッキングされた手の骨組みの描画を管理
  • OVRMesh: 描画する手のメッシュの状態を管理
  • OVRMeshRenderer: 描画する手のメッシュの描画を管理

これらに補足すると、骨組みにはそれを構成するゲームオブジェクト(シリンダ型)が内包されておりこれを使ってお当たり判定などを実装している模様です。
これらを理解した上で、今回の目標であるVR内オブジェクトをつかむためにOVRHandクラスとOVRSkeletonクラスオブジェクトを利用して実装していきます。
またちょっとした知識で、インスペクタからOVRMeshRendererを無効にしてOVRSkeletonRendererを有効にすることで手の描画が骨組みのみにすることができます。

オブジェクトを指でつまめるようにするために、つままれるオブジェクトにアタッチするスクリプトを作成します。
まずはAssets直下にScriptsフォルダを作成してその中にC#スクリプトPinchableObjectを作成します。  

Assets/Scripts/PinchableObject.cs

作成できたら以下のプログラムを記述します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PinchableObject : MonoBehaviour
{   
    public GameObject HandPrefab;

    private OVRHand hand;
    private OVRSkeleton skeleton;
    private Renderer renderer;
    private bool isTouching = false;
    private bool initialized = false;

    void Start()
    {
        this.hand = this.HandPrefab.GetComponent<OVRHand>(); 
        this.skeleton = this.HandPrefab.GetComponent<OVRSkeleton>();
        this.renderer = this.GetComponent<Renderer>();
        this.initialized = this.hand != null && this.skeleton != null;

        // もし必要スクリプトがなければオブジェクトを黄色にする
        if(!this.initialized) this.renderer.material.color = Color.yellow; 
    }

    public void OnTriggerEnter(Collider collider)
    {   
        if(!this.initialized) return;
        this.isTouching = true;
    }

    public void OnTriggerStay(Collider collider)
    {
        if(!this.initialized) return;
        this.isTouching = true;
    }

    public void OnTriggerExit(Collider collider)
    {   
        if(!this.initialized) return;
        this.GetComponent<Renderer>().material.color = Color.white;
        this.isTouching = false;
    }

    void Update()
    {   
        if(!this.initialized) return;
        bool isPinching = this.hand.GetFingerIsPinching(OVRHand.HandFinger.Thumb);

        if(this.isTouching && isPinching){
            var newPos = this.skeleton.Capsules[5].CapsuleCollider.transform.position;
            
            this.GetComponent<Renderer>().material.color = Color.red;
            this.transform.position = newPos;
        }
    }
}

簡単に説明すると、『オブジェクトをつまむ』を実装するには手がオブジェクトに触れていて指がつまむ動作(今回は親指といずれかの指がくっついた時)を認識できれば良いので、OVRSkeletonに内包されたシリンダ型オブジェクト群で当たり判定をして、OVRHandクラスのGetFingerIsPinching()メソッドで親指とその他の指がくっついているかを取得することで実装しています。
つまんでいるときのオブジェクトの位置は、骨組みのシリンダ型オブジェクトリストから最もそれらしい位置に移動させるようにプログラムしています。
まだ明確にどのインデックスにどのシリンダが格納されているか分からないので、調査が必要です。
今回はブログ用に最低限のプログラムでの実装でしたが、用意されたクラスでより厳密にプログラムすることも可能だと思われます。

スクリプトができたのでゲームオブジェクトの配置とアタッチや各種設定をしていきます。
まずはつまむオブジェクトを作成しましょう。
ヒエラルキーOVRCameraRigの外に適当なオブジェクトを作成しコライダを設定します。
オブジェクトの位置はOVRCameraRIgにできるだけ近づけて、小さめにするとVR空間で見つけやすくなります。

オブジェクトの生成

次にオブジェクトに前章スクリプトをアタッチします。
今回は右手のみでつまめるようにするため、アタッチ後にPinchableObjectスクリプトHandPrefabに右手のOVRHandPrefabドラッグ&ドロップして充てます。

アタッチして設定

ここまでできたら、つままれるオブジェクトの設定は終わりです。
次につまむ手にコライダを設定します。
手に当たり判定を与えるには手のメッシュにコライダを充てるのではなく、骨格に紐づいたゲームオブジェクト(シリンダ型)にコライダを設定します。
既にSDKスクリプトでコライダ自体は充てられるようになっているので、設定は下の図のようにOVRHandPrefabOVRSkeletonスクリプトの設定をGUIで行うだけです。

物理演算の有効化

さて、ここまで来ればあとは動作確認ができます。
File > Build And Runで実機にアプリを送り以下の動画のように操作できれば実装完了です。

掴んでみる!

たまに勝手にオブジェクトを手放してしまうような挙動がありますが、手を早く動かしたり背景や角度的に手が認識できないことが多々あるようなのでその辺の対応も必要になってくるのかもしれません。

まとめ

  • 既に便利なクラスが用意されていて思っていたより簡単に実装できた。
  • 指のシリンダ型オブジェクトの明確な呼び出し方が知りたい。
  • 手を早く動かしたりすると認識ができなくなるので注意。

コメント

このブログの人気の投稿

レイクタウンにある生産緑地

越谷レイクタウンに新たに民間キャンプ場がオープン

Phaser3 + Typescriptを使ってRPGゲームの基礎を作ろう!その2