前回に引き続き、Oculus Quest のパススルーを試します。 せっかくなのでちょっとAR/MRっぽいものをということで「ものさし」を作ってみます。
ものさしとはいっても「両手の人差し指の間を測って表示する」という機能です。 両手の人差し指で計測したいモノを挟めばその長さがわかるというものです。
作り方
1. 事前準備
最初に、Unity でパススルーのサンプル AugmentedObjects シーンが動作するところまでやります。 今回はこの AugmentedObjects シーンにものさしの機能を追加していきます。
このあたりのセットアップ方法に関しては Unity + Oculus Quest 2 開発メモ - フレームシンセシス がとても参考になるのでぜひそちらをご参照ください。
2. ハンドトラッキングを利用可能にする
指を使うのでハンドトラッキングを有効化します。
- Hierarchy ウインドウで OVRCameraRig/TrackingSpace/LeftHandAnchor の下に OVRHandPrefab.prefab (手のオブジェクト) を追加する。 OVRHandPrefab.prefab は Assets/Oculus/VR/Prefabs/ にある。
- 同様に OVRCameraRig/TrackingSpace/RightHandAnchor にも OVRHandPrefab.prefab を追加する。 こちらは右手用なので、Inspector を開いて Hand Left となっているプロパティ値をすべて Hand Right に変更。
- OVRCameraRig で Inspector を開いて「OVR Manager - Hand Tracking Support」を Hands Only に設定。
3. ものさしを追加する
次にものさし機能に関するオブジェクトを追加していきます。
Hierarchy ウインドウで空オブジェクトを追加 (Create Empty) して Ruler という名前に変える。
Ruler オブジェクトの Inspector を開いて、Add Component で Hands Manager (Assets/Oculus/SampleFramework/Core/HandsInteraction/Scripts/HandsManager.cs) を追加。 Left Hand と Right Hand に OVRCameraRig に追加した OVRHandPrefab を指定する。
Project を開いて、適当なフォルダで Ruler.cs スクリプトを作成を作成 (Create - C# Script) する。 作成したら、Ruler オブジェクトに Add Component で追加する。
Ruler オブジェクトの下に Line を追加 (Effects - Line)。デフォルトのままだと大きすぎるので、Positions を変更して少し小さめにしておく。
Rulerの下に、長さを表示するためのテキストを追加 (3D Object - Text-TextMeshPro)。その際自動的に追加された Canvas の Inspector を開いて以下を修正。
- 「Canvas - Render Mode」を World Space に変更する。
- 「Rect Transform - Scale」を X, Y, Z いずれも 0.0015 に変更する。
- Canvas の下に追加されたテキストの名前を SizeText に変更する。
4. スクリプトを書く
スクリプトは以下のような感じになります。
using OculusSampleFramework;
using TMPro;
using UnityEngine;
public class Ruler : MonoBehaviour
{
private Transform cameraTransform; // カメラ(目)の位置
private OVRSkeleton left; // 左手
private OVRSkeleton right; // 右手
private LineRenderer line; // 右手人差し指と左手人差し指をつなぐ線
private TMP_Text sizeText; // 長さ表示用テキスト
private Transform sizeTextTransform; // 長さ表示用テキストの位置
void Start()
{
cameraTransform = Camera.main.transform;
left = HandsManager.Instance.LeftHand.GetComponent<OVRSkeleton>();
right = HandsManager.Instance.RightHand.GetComponent<OVRSkeleton>();
line = transform.Find("Line").GetComponent<LineRenderer>();
sizeText = transform.Find("Canvas/SizeText").GetComponent<TMP_Text>();
sizeTextTransform = transform.Find("Canvas");
}
void Update()
{
// 人差し指の位置を取得する
var leftPos = left.Bones[(int)OVRSkeleton.BoneId.Hand_IndexTip].Transform.position;
var rightPos = right.Bones[(int)OVRSkeleton.BoneId.Hand_IndexTip].Transform.position;
// 右手人差し指と左手人差し指の間に線を描く
line.SetPosition(0, leftPos);
line.SetPosition(1, rightPos);
// 両手人差し指の間の距離を測る
float diff = Vector3.Distance(leftPos, rightPos);
int distance = Mathf.RoundToInt(diff * 100f); // cm単位にする
// 長さ表示用テキストを目の前40cmの場所に配置する
sizeTextTransform.position = cameraTransform.position + cameraTransform.forward * 0.4f;
sizeTextTransform.LookAt(cameraTransform);
sizeTextTransform.Rotate(new Vector3(0f, 180f, 0f));
sizeText.text = $"{distance}cm";
}
}