4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Meta Quest2のハンドトラッキングでハンドを貫通しないようにする

Last updated at Posted at 2023-09-01

はじめに

概要

本記事では、Unityを用いたMeta Quest2用のVRコンテンツにおいて、ハンドトラッキングを使用する際にハンドモデルがオブジェクトに触れた際に貫通しないようにする手法を記載します。

本記事では左手のみの解説ですが、右手も同様にして下さい。

本記事のゴール

ハンドトラッキングする際に、他のオブジェクトに触れた際に貫通しないようになります。
下の動画では、貫通する手がハンドトラッキングによる実際の手の位置、貫通しない手が本記事で作成する手です。
Test18-4_HandTest - SampleScene - Android - Unity 2021.3.25f1 Personal DX11 2023-08-29 23-56-16.gif

動作環境

本記事内容を作成したのは下記環境です。

  • Unity 2021.3.25f1
  • Oculus Integration 55.0

仕組み

OVRHandsの作り

OVRHandsは、以下のような階層となっています。
その中で、手のメッシュはl_handMeshNode及びr_handMeshNodeが該当しています。
このメッシュはb_l_wristまたはb_r_wrist配下の各オブジェクト(ボーン)と対応しており、これらのTransformがメッシュに反映され、手の位置や形が変化するようです。

各ボーンと手の位置は以下が分かりやすいので参照にしてください。

ハンドトラッキングによる手や指のTransform情報は、LeftHandやRightHandにアタッチされているHandコンポーネント等によりボーンに反映されています。

手を貫通しないようにするための作戦

OVRHandsをそのまま使用すると実際の手の位置が反映され、手がオブジェクトをすり抜けてしまいます。
そのため、もう一つ手(OVRHandsSlave)を用意し、それをOVRHandsに追随するように動かすとともに、コライダをつけて衝突判定をさせてオブジェクトを貫通しないようにします。
OVRHandsに追随させるためには、Fixed Jointを使用します。

作成方法

1. Unityプロジェクトの作成~ハンドトラッキングの設定

下記記事を前提としているためそちらを参照してください。

2. 新たなOVRHandsの追加

 Projectビューから下記を探し、HierarchyのOVRInteractionにドラッグ&ドロップで追加し、名称を「OVRHandsSlave」に変更する。
 Assets/Oculus/Interaction/OVRIntegration/Runtine/Prefabs/OVRHands.prefab

01_OVRHandsSlave追加.png

3. OVRHandsSlaveのLeftHand配下の各オブジェクトの非アクティブ化

 OVRHandsSlave/LeftHand配下にある下記オブジェクトを非アクティブ化する。
  ・HandDataLeft
  ・HandFeaturesLeft
  ・HandInteractionsLeft
  ・OVRHandDataSource

02_OVRHandsSlave非アクティブ.png

4. OVRHandsSlaveのLeftHandのコンポーネントの無効化

 OVRHandSlave/LeftHandの下記コンポーネントを無効化する。
  ・Hand
  ・Hand Active State

03_LeftHand非アクティブ.png

5. OVRHandsSlaveのOVRLeftHandVisualのコンポーネントの無効化

 OVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisualの下記コンポーネントを無効化する。
  ・Hand Visual

04_LeftHandVisual非アクティブ.png

6. OVRHandsSlaveのOculusHand_Lのコンポーネントの無効化

 OVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_Lの下記コンポーネントを無効化する。
  ・Animator
05_OculusHand_L非アクティブ.png

7. OVRHandsのOculusHand_LにRigidbodyコンポーネント追加

 OVRHands/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_LにRigidbodyコンポーネントを追加する。
  Is Kinematicにチェックをつける。
06_OculusHand_L_Rigidbody追加.png

8. OVRHandsSlaveのOculusHand_LにRigidbodyとFixed Jointコンポーネント追加

 OVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_LにRigidbodyコンポーネントとFixed Jointコンポーネントを追加する。
  RigidbodyのFreeze Rotation XYZにチェックをつける。
  Fixed JointのConnected BodyにOVRHands/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_LのRigidbodyを設定する。

07_OculusHand_L_Rigidbody,FixedJoint追加.png

9. OVRHandsSlaveのOculusHand_LにVirturl Handコンポーネント追加

 OVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_Lに、下記Virtual Handを追加する。

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

public class VirtualHand : MonoBehaviour
{
    [SerializeField] Transform sourceOculusHand;
    [SerializeField] Transform OculusHand;
    [SerializeField] private Transform rootSourceBone;
    [SerializeField] private Transform rootBone;
    [SerializeField] private Vector3 adjustRotate;

    [SerializeField] private Rigidbody OculusHandRB;

    private List<Transform> _sourceBones;
    private List<Transform> _bones;

    public bool isSetBoans = true;

    void Start()
    {
        _sourceBones = new List<Transform>();
        _bones = new List<Transform>();
        GetBones(rootSourceBone, _sourceBones);
        GetBones(rootBone, _bones);
    }

    void Update()
    {
        OculusHand.localScale = sourceOculusHand.localScale;
        if (isSetBoans)
        {
            SetBones();
        }
    }

    private void FixedUpdate()
    {
        SetHand();
    }

    private void SetHand()
    {
        OculusHandRB.MoveRotation(sourceOculusHand.rotation * Quaternion.Euler(adjustRotate));
    }

    private void SetBones()
    {
        if (_sourceBones.Count == _bones.Count)
        {
            for (int i = 0; i < _sourceBones.Count; i++)
            {
                _bones[i].localRotation = _sourceBones[i].localRotation;
            }
        }
    }

    private void GetBones(Transform rootBone, List<Transform> bone)
    {
        int length = rootBone.childCount;

        for (int i = 0; i < length; i++)
        {
            Transform b = rootBone.GetChild(i);
            if(b.name.IndexOf("b_") == 0)
            {
                bone.Add(b);

                if (b.childCount > 0)
                {
                    GetBones(b, bone);
                }
            }
        }
    }
}

  Source Oculus HandにOVRHands/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_Lを設定
  Root Source BoneにOVRHands/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_L/b_l_wristを設定
  Root BoneにOVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_L/b_l_wristを設定
  Adjust RotateのYを180に設定
  Oculus Hand RBに、OVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_LのRigidbodyを設定

08_OculusHand_LのVirtualHand追加_修正.png

10. OVRHandsSlaveのb_l_wrist以下にCapsule Collider追加

 OVRHandsSlave/LeftHand/HandVisualLeft/OVRLeftHandVisual/OculusHand_L/b_l_wrist以下に、Capsule Colliderを追加する。
 ここら辺は実装によりやり方があると思いますが、今回は各ボーンごとに新たに「coll_」で始まるオブジェクトを作成し、それぞれにCapsule Colliderコンポーネントをアタッチ。
 指の関節を意識して位置や大きさを調整。

09_OculusHand_Lのコライダ追加.png

10_OculusHand_Lのコライダ.png

おわりに

以上で、左手において貫通しない手が実装できるようになります。
右手は上記説明の「Left」を「Right」に、「b_l_wrist」を「b_r_wrist」に読み替えて同様にすればOKです。

4
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?