【Meta Quest3】UnityでARお絵かきアプリの作り方

VR・AR

本記事で紹介している情報は執筆時点のものであり、閲覧時点では変更になっている場合があります。また、ご利用の環境によっては本記事の情報通りに動作しない場合がございます。あらかじめご了承ください。

前書き

 こんにちは、シィグです。今回は先日発売された「Meta Quest3」を用いて現実世界にお絵描きができる「ARお絵描きアプリ」を作成しましたので、作り方を解説したいと思います。

 本記事では、「Oculus Quest3」で開発を進めていますが、「Meta Quest Pro」や「Meta Quest2」での動作も確認済みですので、同様に開発可能です。ただし、「Meta Quest2」では、現実世界を見ることのできるパススルー機能がモノクロで解像度も粗いためARにはあまり向かないかもしれません。

この記事で作るもの

 この記事で作れるものは以下の動画のようなものです。親指と人差し指をくっつけることで線を描くことができます。

参考文献

 以下の記事を参考にして作成しました。

事前準備

 まずは、Unity Hubからで3Dのテンプレートを選択し、プロジェクトを作成してください。この際、プロジェクトを作成するエディターには「Android Build Support」「OpenJDK」「Android SDK & NDK Tools」モジュールを追加しておいてください。

Unityの始め方やプロジェクト作り方、モジュールの追加方法については以下の記事を参考にしてください。

Meta Quest用の環境設定

 プロジェクトが作成できましたら、環境設定を行います。

 Meta Quest用の環境設定を行っていきます。右上の「File」から「Build Settings」を選択し、Platformを「Android」に「Swtich Platform」を行ってください。

 次に、左下の「Player Settings」をクリックしてください。

 Project Setting上の「Player」項目を選択し、Androidのタブ内の「Other Settings」の「Rendering」の「Color Space*」を「Linear」に変更してください。

 次に、Player Settings上の一番下の項目である「XR Plugin Management」を選択し、「Install XR Plugin Management」をクリックしてください。インストールが始まりますのでしばらく待ちます。

インストールがなかなか終わらない場合は、画面の切り替えを行うことでインストール後の画面が表示されることがあります。

 インストールが終わりますと、上側に「パソコンのマーク」「漢字の王のようなマーク(?)」「Androidのマーク」のタブ(画像参照)がある画面が表示されると思います。
※エディターに加えているモジュールによって、上記以外のマークがある場合もあります。

 それぞれのタブに対して、Oculus」という項目があると思いますので、それらすべてにチェックを入れてください。

アセット「Oculus Integration」のインストール

 続いて、Meta Questの開発を進めるうえで便利なアセット「Oculus Integration」のインストールを行っていきます。
 アセットのインストール方法については、以下の記事を参考にして下さい。

 ダウンロード後の「Import」をクリックする部分まで進めてください。

 インポート時に、メッセージが出てきますので、以下の選択肢を選び、進めてください。
 「Yes, for these and other files that might be found later」→「Don`t send」→「Show Asset (Recommended)」→「Clean Up (Recommended)」→「Restart」→「Upgrade」→「Restart」

 Unityが再起動し、プロジェクトビューに「Oculus」フォルダが作成されます。

ハンドトラッキングを実装する

 インポートしたアセット「Oculus Integration」を用いて、ハンドトラッキングの実装を行います。

 Assets/Oculus/VR/Prefabs上にあるOVRCameraRig」プレハブをヒエラルキー上に配置してください。

 設置した「OVRCameraRig」のインスペクターを設定を行います。

「OVR Manager (Script)」
・Target Deviceの「Quest 3」にチェックを入れる
・Quest Featuresで「General」タブを選択し、「Hand Tracking Support」を「Hands Only」に変更する

 次に表示される手を追加します。

 Asset/Oculus/VR/Prefabs上にあるOVRCustomHandPrefab_L」と「OVRCustomHandPrefab_R」を先程設置した「OVRCameraRig」の孫である「LeftHandAnchor」と「RightHandAnchor」の子として、それぞれ設置してください。

 「OVRCustomHandPrefab_L」のインスペクター上「OVR Custom Skeleton (Script)」にあるAuto Map Bones」をクリックしてください。
 「OVRCustomHandPrefab_R」でも同様にAuto Map Bones」をクリックしてください。

 最後にヒエラルキー上にある「Main Camera」は使用しないので削除してください。

 以上でハンドトラッキングの実装は完了です。

ハンドトラッキングで線を描く

 ハンドトラッキングで線を描く方法について記述していきます。

Line Rendereの用意

 線の描画には「LineRendere」を用います。まず、ヒエラルキー上で「空のオブジェクト」を作成してください。(名前は「Line」にしました。)
 作成した空のオブジェクトのインスペクターで「Add Component」をクリックし、「Line Rendere」コンポーネントを加えてください。
 コンポーネントを加えるとオブジェクトが出現すると思います。これが線となるオブジェクトになります。

 次に、線の見た目を変更していきます。

 まず、線の色を変更します。
 プロジェクトビューで「Materials」という名前でフォルダを作成し、その中に「Material」を作成し好きな色を設定してください。
 そして、それを先程作成した「空のオブジェクト(Line)」にドラッグ&ドロップしてください。

 次に、線の太さを変更します。
 先程追加した「Line Rendere」上で「Width」の項目を見つけ、線の左側にあるひし形をドラッグし、好きな太さに変更してください。(グラフは、右クリックをしてからマウスのホイールを回すことで拡大できます。)
 最後に、「Line Rendere」上で「Positions」をクリックし、表示された「Size」を0に変更してください。

 次に「空のオブジェクト(Line)」のプレハブ化を行います。

 プロジェクトビューに「Prefabs」という名前のフォルダを作ってください。そして、そのフォルダの中に先程作成した「空のオブジェクト(Line)」をドラッグ&ドロップして入れてください。

 ヒエラルキー上にある「空のオブジェクト(Line)」は使用しないので、削除してください。


 以上でLine Rendererの準備は完了です。

線を描くプログラムを書く

 次に線を描くプログラムを書いていきます。

 プロジェクトビューで「Scripts」という名前でフォルダを作ってください。そして、その中にDroawing」という名前でC# Scriptファイルを作成してください。

 次にプログラムを書いていきます。効率よくプログラムを書くために使用する「Visual Studio」のインストール方法については、以下の記事を参考にしてください。

 作成したC# Scriptファイルをテキストエディタで開き、以下のプログラムを記述してください。

using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using static OVRPlugin;

public class Droawing : MonoBehaviour
{
    [SerializeField] GameObject lineObjectPrefab; //プレハブ化した空のオブジェクト(Line)を入れる
    private GameObject currentLineObject = null;

    [SerializeField] private GameObject handObject; //書きたい方の手を入れる
    private OVRHand _hand; //ハンドトラッキングをしているかの判定に使う
    private OVRSkeleton _skeleton; //Bone情報
    [SerializeField] private float touchDistanceThreshold = 0.02f; //人差し指と親指がこの値以上近づいたら線を書く

    private void Start()
    {
        _hand = handObject.GetComponent<OVRHand>();
        _skeleton = handObject.GetComponent<OVRSkeleton>();
    }

    void Update()
    {
        if (!_hand.IsTracked) //ハンドトラッキングしているかの確認
        {
            return;
        }

        var indexTipPos = _skeleton.Bones[(int)OVRSkeleton.BoneId.Hand_IndexTip].Transform.position; //人差し指先端の位置取得
        var thumbTipPos = _skeleton.Bones[(int)OVRSkeleton.BoneId.Hand_ThumbTip].Transform.position; //親指先端の位置取得

        float distanceR = Vector3.Distance(indexTipPos, thumbTipPos); //人差し指先端と親指先端の距離

        if (distanceR < touchDistanceThreshold) //線を書く処理
        {
            if (currentLineObject == null)
            {
                currentLineObject = Instantiate(lineObjectPrefab, Vector3.zero, Quaternion.identity);
            }
            LineRenderer lineRenderer = currentLineObject.GetComponent<LineRenderer>();

            int nextPositionIndex = lineRenderer.positionCount;
            lineRenderer.positionCount = nextPositionIndex + 1;
            lineRenderer.SetPosition(nextPositionIndex, indexTipPos);
        }
        else //線を書かない時の処理
        {
            if (currentLineObject != null)
            {
                currentLineObject = null;
            }
        }
    }
}
線を描く部分のプログラムについての解説です。

Update関数内について、コメント毎の解説を行います。

まず、一つ目のコメント「ハンドトラッキングしているかの確認」について、

if (!_hand.IsTracked) //ハンドトラッキングしているかの確認
{
    return;
}

 この部分では、「Oculus Integration」アセットが用意してくれている「OVRHand」というスクリプトで、ハンドトラッキングしているかをBool値で取得できる関数「IsTracked」を用いてハンドトラッキングをしているかの確認を行っています。
 ハンドトラッキングをしていない場合は、「return」を行い、以降の処理に進まないようにしています。
 このようにすることによって、エラーの発生を防いでいます。


 次に、二つ目のコメント「人差し指先端と親指先端の位置取得」について、

//人差し指先端と親指先端の位置取得
var indexTipPos = _skeleton.Bones[(int)OVRSkeleton.BoneId.Hand_IndexTip].Transform.position;
var thumbTipPos = _skeleton.Bones[(int)OVRSkeleton.BoneId.Hand_ThumbTip].Transform.position;

 この部分については、コメントの通り「indexTipPos」と「thumbTipPos」変数にそれぞれ人差し指先端と親指先端の位置を代入します。これらの変数は、人差し指先端と親指先端がくっついているかどうかの判定に用います。
 他の指や先端以外の位置を取得したい方は、以下の記事が参考になると思います。


 次に、三つ目のコメント「人差し指先端と親指先端の距離」について、

float distanceR = Vector3.Distance(indexTipPos, thumbTipPos); //人差し指先端と親指先端の距離

 この部分には、先程取得した人差し指先端と親指先端の位置からそれらの間の距離を計算しています。これは、線を描くか描かかないかの判定に用います。


 次に、四つ目のコメント「線を描く処理」について、

if (distanceR < touchDistanceThreshold) //線を書く処理
{
    if (currentLineObject == null)
    {
        currentLineObject = Instantiate(lineObjectPrefab, Vector3.zero, Quaternion.identity);
    }
    LineRenderer lineRenderer = currentLineObject.GetComponent<LineRenderer>();

    int nextPositionIndex = lineRenderer.positionCount;
    lineRenderer.positionCount = nextPositionIndex + 1;
    lineRenderer.SetPosition(nextPositionIndex, indexTipPos);
}

 この部分は、まず、最初のif文で人差し指先端と親指先端の距離が変数「touchDistanceThreshold」の値より小さいかを調べ、くっついているかどうかを判定しています。

 次のif文では、線の書き始めかどうかを判定しています。今回作成するプログラムでは、線は一筆で書いている部分を一つのオブジェクトとして生成しています。
 つまり、書き始めの時は、新しく線となるオブジェクト(先程プレハブ化した「空のオブジェクト(Line)」)を生成し、書き始めでない、つまり、線を連続して書いている場合は、新しくオブジェクトの生成はしないようにしています。

 二つ目のif文の後は、「LineRenderer」コンポーネントを取得し、親指先端の位置まで新しく線を描く処理です。「LineRenderer」コンポーネントでは、座標と配列を用いて、配列に含まれる座標をつなげる線を描画します。親指先端の位置を新しく配列に加えることで線を描いているように見せています。


 最後のコメント「線を描かない時の処理」について、

else //線を書かない時の処理
{
    if (currentLineObject != null)
    {
        currentLineObject = null;
    }
}

この部分では、人差し指先端と親指先端が離れた際に、前に線を描いていたオブジェクトを変数「currentLineObject」から削除しています。すべての線がつながってしまうことを防止しています。

 以上がプログラムの解説でした。

 プログラムが書けましたら、保存し、Unityの画面に戻ってください。

プログラムとオブジェクトを関連付ける

 先程書いたプログラムをオブジェクトと関連付けていきます。

 まず、ヒエラルキー上に「空のオブジェクト」を作成してください。(名前は「DroawingManager」としました。)
 そして、先程書いたプログラムのC# Scriptファイル「Droawing」を「空のオブジェクト(DroawingManager)」にドラッグ&ドロップし、アタッチしてください。

「Droawing (Script)」コンポーネントで次の作業を行ってください。

Droawing (Script)
・「Line Object Prefab」にプロジェクトビューのAssets/Prefabsフォルダ内にある「空のオブジェクト(Line)」をドラッグ&ドロップする。
・「Hand Object」にヒエラルキー上のOVRCameraRig/TrackingSpace/RightHandAnchorオブジェクトの子の「OVRCustomHandPrefab_R」をドラッグ&ドロップする。(左手で描きたい場合は、「OVRCustomHandPrefab_L」)

 以上がプログラムとオブジェクトを関連付ける作業です。

以上の作業を行うことでハンドトラッキングを用いて線を描くことができるようになります。

パススルー機能をオンにする

 次は、Oculus Questのパススルー機能をオンにする方法を解説します。

Quest Linkによるテストプレイはパススルー機能をオンにするとできなくなります。パススルー機能を使用する際のテストプレイはBuildを行ってください。

 まずメニューバーのEdit→Project Settingをクリックし、開いてください。
 次に、「Player」の項目を選択し、Androidタブの中のOther Setting→Configurationの「Scripting Backend」を「IL2CPP」に変更してください。
 さらに、Other Setting→Configuration→Target Architecturesの「ARMv7、ARM64、x86 (Chrome OS)、x86-64 (Chorme OS and Magic Leap 2)」の内ARM64」のみにチェックを入れてください。

 次に、ヒエラルキー上にある「OVRCameraRig」の「OVR Manager (Script)」コンポーネントの設定を以下のように変更してください。

OVR Manager (Script)
・Quest Features→General上の「Anchor Support」を「Enable」に変更する。
・Quest Features→General上の「Passthrough Support」を「Required」に変更する。
・「Enable Passthrough」にチェックマークを入れる。

 次に、「OVRCameraRig」のインスペクターの「Add Component」をクリックし、「OVR Passthrough Layer」を検索し、OVR Passthrough Layer (Script)」コンポーネントを追加してください。
 

 追加した「OVR Passthrough Layer (Script)」コンポーネントの「Placement」を「Underlay」に変更してください。

 最後にカメラの設定を行います。ヒエラルキー上のOVRCameraRig→TrackingSpaceの子の「CenterEyeAnchor」を選択してください。

Camera
・「Clear Flags」を「Solid Color」に変更する。
・「Background」のR,G,B,Aの値をすべて0に変更する。

 以上でパススルー機能をオンにする設定は完了です。

ビルドして実行する

 パソコンとMeta Questをケーブルでつなぎ、Fileの「Build And Run」をクリックしてください。Meta Questでプロジェクトが起動し、パススルーを用いた現実世界にお絵描きできるアプリが動くと思います。

Meta Questでビルドしたプロジェクトを動かすためには、Meta Questアプリから「開発者モード」をオンにする必要があります。

この記事の内容はここまでですが、好みに合わせてアレンジしてみてください。

 私は、手の色を薄くし、色を変えられるパレットオブジェクトを作ってみました。

終わりに -読まなくてもOKです-

 今回は、パススルー機能の強化されたMeta Quest3を用いて、現実世界にお絵描き「ARお絵描きアプリ」の作成を行いました。
 パススルー機能を用いた開発は意外にも簡単な設定で行えるため、お絵描き以外もAR化してみると意外に面白くなるかもしれないので是非挑戦してみてください。
 私の方でも何か作りましたらまた共有していきたいと思います。

 記事の内容などについて、質問や感想等がありましたら気軽にコメントしてくれればと思います。

コメント

タイトルとURLをコピーしました