【Unity(C#)】ハンドトラッキングで簡易版VRお絵かきアプリ


ハンドトラッキング

OculusQuestにとんでもないテクノロジーがやってきました。(今更)
まだベータ版みたいですが、ハンドトラッキングがアップデートで実装されました。(今更)

お勉強がてら中身をいじってみようと思います。
今回は下記記事を参考に、簡易版お絵かきアプリを実装してみようと思います。

【参考リンク】:OculusQuest ハンドトラッキングSDKから、指Boneの情報を取得し分析する

デモ

なかなかのクオリティですがやりたいことができました。

実装方法としては、
人差し指を完全に伸ばした状態のときにTrailRendererEmittingtrueに、
それ以外の時はfalseにしているだけです。

コード

using UnityEngine;
/// <summary>
/// 適当にオブジェクト作ってアタッチ
/// </summary>
[RequireComponent(typeof(TrailRenderer))]
public class DrawFunction : MonoBehaviour
{
    [SerializeField]
    OVRHand m_oVRHand;

    [SerializeField]
    OVRSkeleton m_ovrSkeleton;

    [SerializeField]
    OVRHand.HandFinger m_handFingerType;

    TrailRenderer m_tr;

    void Reset()
    {
        m_tr = this.gameObject.GetComponent<TrailRenderer>();
        m_tr.time = Mathf.Infinity;
        m_tr.widthMultiplier = 0.01f;
        m_tr.minVertexDistance = 0.01f;
    }

    void Start()
    {
        m_tr = this.gameObject.GetComponent<TrailRenderer>();
    }

    void Update()
    {
        Vector3 indexTipPos = m_ovrSkeleton.Bones[(int)OVRSkeleton.BoneId.Hand_IndexTip].Transform.position;
        this.gameObject.transform.position = indexTipPos;

        if (m_oVRHand.GetFingerPinchStrength(m_handFingerType) == 0)
        {
            m_tr.emitting = true;
        }
        else
        {
            m_tr.emitting = false;
        }
    }
}

TrailRenderer

Unity様が用意した神コンポーネントのおかげでお絵かき機能自体は
ノーコーディングで実装できています。

細かい設定はResetメソッドの中で行っています。
RequireComponentで勝手に引っ付いてくるようにして、
Resetで初期設定を行う王道スタイルです。

    TrailRenderer m_tr;

    void Reset()
    {
        m_tr = this.gameObject.GetComponent<TrailRenderer>();
        m_tr.time = Mathf.Infinity; //消失時間の設定 Infで無限に存在(消失しない)
        m_tr.widthMultiplier = 0.01f; //線の太さ
        m_tr.minVertexDistance = 0.01f; //頂点間の距離 曲線の滑らかさに起因
    }

BoneId

お絵かきするためのTrailRendererの位置を人差し指の指先に合わせます。
先述の参考リンクにもあるように、下記でそれぞれの関節の位置を取得できます。

人差し指の先端場合
Vector3 indexTipPos = m_ovrSkeleton.Bones[(int)OVRSkeleton.BoneId.Hand_IndexTip].Transform.position;

Editor上でPlayModeにした場合(LINKなど無しで)、
ArgumentOutOfRangeExceptionが出ます。気にせずビルドしたらいけました。

GetFingerPinchStrength

今回は指を伸ばした状態でのみ、お絵かきを可能にしました。
最初はGetFingerIsPinchingで引数にIndexを指定していましたが、
指を曲げている、曲げていない の判定が少し厳しかったので断念しました。

なので、指を完全に伸ばした状態(GetFingerPinchStrength == 0)を判定に用いることにしました。

思いのほかうまくいったので今回のお遊びはここまでとしました。

その他詰まった箇所

ビルド後立ち上げたアプリで"ハンドトラッキング未対応"みたいなのが出る

画像の箇所をHands Only or Controllers And Handsに直せばOKです。

ビルドできない

Android SDKなどのエラーが出てたので
Unityの再インストール(Android SDKなども合わせて)で直りました。
LTSの最新版入れました。

まとめ

今回は超簡易版として作りましたので、
既存のコンポーネントであるTraiRendererを使用しています。

色を変えたりは簡単に実装できそうですが、
消しゴム機能を実装する際にTraiRendererで可能なのか?
というのが懸念の一つです。

消しゴム機能で好きな箇所だけ自由に消したい...となると
お絵かき機能自体を自前で用意しないと厳しいかもしれません。

何か良い方法あればアドバイスください。


追記 2020/03/22

消しゴム機能ですが、Redo(ひとつ前に戻る)という形で対応してみました。

やり方はシンプルで、手が任意のジェスチャーに変化した際に
TrailRederer付きゲームオブジェクトを生成しているだけです。

なので、一筆ごとにTrailRendererが生成されているような状態になります。

【参考リンク】:【Unity(C#)】TrailRendererの頂点座標を取得する方法


追記 2021/08/15
生成時にメッシュを作成して当たり判定を取れば一筆単位ですが消せました。

【参考リンク】:【Unity(C#)】TrailRendererにMeshColliderを付与する