ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【Unity / Oculus Go】Oculus Goコントローラー表示方法とボタン取得方法

rikoubou.hatenablog.com

 前回の記事でOculus Go向けのビルドはできるようになりました。
 今回は前回のプロジェクト(と言ってもCubeやSphereを置いただけですが)にOculus Goコントローラーを追加していきます。


1:必要なアセットのインポート
 プロジェクトを開いたらメニューの「Window」→「Asset Store」を選択します。
f:id:rikoubou:20180603202718p:plain

 するとAsset Storeの画面が出てくるので「Oculus Integration」と入力して検索ボタンを押します。
f:id:rikoubou:20180603203507p:plain

 Oculus Integrationのアセットをダウンロードしてない場合はダウンロードします。

 ダウンロードが終わったら「Import」ボタンを押してアセットをインポートします。
f:id:rikoubou:20180603203553p:plain

 インポートの途中で以下のダイアログが出てきた場合は「Yes」ボタンを押します。
f:id:rikoubou:20180603203748p:plain

 そのあとUnityを再起動するかを尋ねるダイアログが出てくるので、それもYesボタンをクリックしてUnityを再起動させます。

 Unityの再起動が終わって開いていたプロジェクトが再び立ち上がると、Assets配下に「Oculus」というフォルダが追加されています。この中にOculus向けアプリ開発に必要なものが入っています。
f:id:rikoubou:20180603204253p:plain

 これでアセットのインポートは完了です。


2:OVRCameraRigの追加しMain Cameraを削除する
 初めにOVRCameraRigというVR用のカメラを追加します。

「Oculus」→「VR」→「Prefabs」の中にある「OVRCameraRig」をドラッグ&ドロップでSceneに追加します。
f:id:rikoubou:20180604004355p:plain

 これで元のカメラがいらなくなるので「Main Camera」を選択した状態で右クリックし、「Delete」を選択して削除します。
f:id:rikoubou:20180604005054p:plain


3:Oculus Goコントローラーの追加
 次はOculus Go用のコントローラーを追加します。

「Oculus」→「VR」→「Prefabs」の中にある「TrackedRemote」を「OVRCameraRig」の中にある「LeftHandAnchor」と「RightHandAnchor」にドラッグ&ドロップします。
f:id:rikoubou:20180604005504p:plain

「LeftHandAnchor」と「RightHandAnchor」の両方に配置しておくことで、右手・左手の設定から自動的にどちらを表示するかを決めてくれます。

 追加した「TrackedRemote」の子として「OculusGoControllerModel」というのがあります。これがOculus Goのコントローラーになります。
f:id:rikoubou:20180604010707p:plain

 単にコントローラーを追加するだけならこれで終わりです。


4:Oculus Goコントローラーからレーザーポインターを出す
 追加したコントローラーがどこを指しているかがわかるように、コントローラーの先からレーザーポインターを出すようにします。

 最初に「Assets」フォルダを選択した状態で右クリックをし「Create」→「Folder」を選択し、「Scripts」という名前のフォルダを作成します。ここにスクリプトを追加していきます。
f:id:rikoubou:20180603235333p:plain

 次に「Scripts」フォルダを選択した状態で右クリックをし「Create」→「C# Script」を選択し、「LaserPointer」という名前で.csファイルを作成します。
f:id:rikoubou:20180603235659p:plain

 作成した「LaserPointer」ファイルをダブルクリックしてエディタを開き、以下のようにソースコードを記述します。

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

/**
 * レーザーポインターを出すクラス
 */
public class LaserPointer : MonoBehaviour {

	[SerializeField]
	private Transform _RightHandAnchor; // 右手

	[SerializeField]
	private Transform _LeftHandAnchor;  // 左手

	[SerializeField]
	private Transform _CenterEyeAnchor; // 目の中心

	[SerializeField]
	private float _MaxDistance = 100.0f; // 距離

	[SerializeField]
	private LineRenderer _LaserPointerRenderer; // LineRenderer

	// コントローラー
	private Transform Pointer {
		get {
			// 現在アクティブなコントローラーを取得
			var controller = OVRInput.GetActiveController();
			if (controller == OVRInput.Controller.RTrackedRemote) {
				return _RightHandAnchor;
			} else if (controller == OVRInput.Controller.LTrackedRemote) {
				return _LeftHandAnchor;
			}
			// どちらも取れなければ目の間からビームが出る
			return _CenterEyeAnchor;
		}
	}

	void Update () {
		var pointer = Pointer; // コントローラーを取得
		// コントローラーがない or LineRendererがなければ何もしない
		if (pointer == null || _LaserPointerRenderer == null) {
			return;
		}
		// コントローラー位置からRayを飛ばす
		Ray pointerRay = new Ray(pointer.position, pointer.forward);

		// レーザーの起点
		_LaserPointerRenderer.SetPosition(0, pointerRay.origin);

		RaycastHit hitInfo;
		if (Physics.Raycast(pointerRay, out hitInfo, _MaxDistance)) {
			// Rayがヒットしたらそこまで
			_LaserPointerRenderer.SetPosition(1, hitInfo.point);
		} else {
			// Rayがヒットしなかったら向いている方向にMaxDistance伸ばす
			_LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MaxDistance);
		}
	}
}

 ソースコードの記述が終わったら、Sceneの何も選択していない状態で右クリックをし「Create Empty」を選択します。
f:id:rikoubou:20180604174152p:plain

 すると空のGame Objectが作成されるので、名前を「LaserPointer」に変更します。
f:id:rikoubou:20180604174438p:plain

 名前を変更した「LaserPointer」のGame ObjectにScriptsフォルダの中にある「LaserPointer」をドラッグ&ドロップします。
f:id:rikoubou:20180604174740p:plain

 するとソースコードが追加され、以下のように「SerializeField」でクラス変数としたものを設定する項目が出てきます。
f:id:rikoubou:20180604175042p:plain

 以下のように「○○Anchor」となっているところに、それぞれ該当する「Anchor」をドラッグ&ドロップします。
f:id:rikoubou:20180604175611p:plain

 次にレーザーを描写するLine Renderderを作成します。

 LaserPointerのGame Objectを選択した状態で、「Add Component」ボタンを押してlineを入力して出てきた「Line Renderer」を選択します。
f:id:rikoubou:20180604175901p:plain

 Line Rendererが追加されます。
f:id:rikoubou:20180604180108p:plain

 以下の赤枠で囲った部分を設定します。
f:id:rikoubou:20180604181011p:plain

 Materialの選択は右側にある「○」のようなマークをクリックし、出てきたウインドウで「sp」と入力し「Sprites-Default」を選択します。
f:id:rikoubou:20180604181342p:plain

 レーザーの距離による太さは、以下のようにグラフ上で「ダブルクリック」することで頂点を追加できるので、追加した頂点をドラッグで移動させます。その後その頂点から伸びている薄い線の先にある点をドラッグして曲線を調節します。
f:id:rikoubou:20180604181527p:plain
f:id:rikoubou:20180604182011g:plain

 レーザーの色や不透明度は、以下のように上下の両端にあるスライダーのチェック部分をクリックで選択し、下側は色、上側はアルファ値を設定します。色やアルファ値は適当でよいです。
f:id:rikoubou:20180604182439p:plain
f:id:rikoubou:20180604182932p:plain

 必要な設定が済んだら、Line Rendererを「Laser Pointer Renderer」にドラッグ&ドロップします。
f:id:rikoubou:20180604183205p:plain

 これでコントローラーの先からレーザーポインターが出ます。

 ちなみにこの状態でビルドしてOculus Goで実行すると以下のようになります。


5:Oculus Goコントローラーのボタン取得方法
 Oculus Goのコントローラーの各ボタンは以下のページから確認できます。
developer.oculus.com

 ボタンの解説画像のみは以下の通り。
f:id:rikoubou:20180604191436p:plain

 画像の通り各ボタンの名前は以下のようになっています。(間違っている可能性あり)

取得ボタン 変数名
トリガー OVRInput.Button.PrimaryIndexTrigger
タッチパッドのクリック OVRInput.Button.PrimaryTouchpad
タッチパッドの上方向スクロール OVRInput.Button.Up
タッチパッドの下方向スクロール OVRInput.Button.down
タッチパッドの左方向スクロール OVRInput.Button.left
タッチパッドの右方向スクロール OVRInput.Button.right
バックボタン OVRInput.Button.Back

 右手・左手のどちらの設定になっているかは以下で取得できます。

var controller = OVRInput.GetActiveController();

if (controller == OVRInput.Controller.RTrackedRemote) {
	// 右手の場合
} else if (controller == OVRInput.Controller.LTrackedRemote) {
	// 左手の場合
}


 各種ボタンの基本的な取得方法は以下のようになります。(間違っている可能性あり)

bool result = OVRInput.Get({ボタン変数名});     // ボタンが押されている間ずっとTrueを返す
bool result = OVRInput.GetDown({ボタン変数名}); // ボタンが押された時の1回のみTrueを返す
bool result = OVRInput.GetUp({ボタン変数名});   // ボタンが離された時の1回のみTrueを返す

 タッチパッド上の2次元座標は以下のように取得します。

Vector2 vector =  OVRInput.Get(OVRInput.Axis2D.PrimaryTouchpad, OVRInput.Controller.RTrackedRemote);
float x = vector.x;
float y = vector.y;


6:Oculus Goコントローラーのボタンでレーザーポインター先のオブジェクトを操作する
 4まででコントローラーからレーザーポインターが出るようになりました。
 5の内容を踏まえて、レーザーポインターの先にあるオブジェクトをボタンで操作できるようにしてみます。

 今回は単純にレーザーポインターが当たっている先のオブジェクトの大きさをボタンで変更できるようにしてみます。

 方法は簡単でScriptsフォルダの中にある「LaserPointer」を開いて以下のように書き換えます。

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

/**
 * レーザーポインターを出すクラス
 */
public class LaserPointer : MonoBehaviour {

	[SerializeField]
	private Transform _RightHandAnchor; // 右手

	[SerializeField]
	private Transform _LeftHandAnchor;  // 左手

	[SerializeField]
	private Transform _CenterEyeAnchor; // 目の中心

	[SerializeField]
	private float _MaxDistance = 100.0f; // 距離

	[SerializeField]
	private LineRenderer _LaserPointerRenderer; // LineRenderer

	// コントローラー
	private Transform Pointer {
		get {
			// 現在アクティブなコントローラーを取得
			var controller = OVRInput.GetActiveController();
			if (controller == OVRInput.Controller.RTrackedRemote) {
				return _RightHandAnchor;
			} else if (controller == OVRInput.Controller.LTrackedRemote) {
				return _LeftHandAnchor;
			}
			// どちらも取れなければ目の間からビームが出る
			return _CenterEyeAnchor;
		}
	}

	void Update () {
		var pointer = Pointer; // コントローラーを取得
		// コントローラーがない or LineRendererがなければ何もしない
		if (pointer == null || _LaserPointerRenderer == null) {
			return;
		}
		// コントローラー位置からRayを飛ばす
		Ray pointerRay = new Ray(pointer.position, pointer.forward);

		// レーザーの起点
		_LaserPointerRenderer.SetPosition(0, pointerRay.origin);

		RaycastHit hitInfo;
		if (Physics.Raycast(pointerRay, out hitInfo, _MaxDistance)) {
			// Rayがヒットしたらそこまで
			_LaserPointerRenderer.SetPosition(1, hitInfo.point);

			/* ---以下追加した部分--- */
			// ヒットしたオブジェクトを取得
			GameObject obj = hitInfo.collider.gameObject;
			// ヒットしたオブジェクトのScaleを取得
			Vector3 scale = obj.transform.localScale;
			
			if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger)) {
				// トリガーボタンを押した時
				Vector3 maxScale = new Vector3(5f,5f,5f);
				// スケールの各値が5より小さい場合は0.1大きくする
				if (scale.sqrMagnitude < maxScale.sqrMagnitude) {
					obj.transform.localScale = new Vector3 (scale.x + 0.1f, scale.y + 0.1f, scale.z + 0.1f);
				}
			} else if (OVRInput.GetDown(OVRInput.Button.PrimaryTouchpad)) {
				// タッチパッドボタンを押した時
				Vector3 minScale = new Vector3(0.5f,0.5f,0.5f);
				// スケールの各値が0.5より大きい場合は0.1小さくする
				if (scale.sqrMagnitude > minScale.sqrMagnitude) {
					obj.transform.localScale = new Vector3 (scale.x - 0.1f, scale.y - 0.1f, scale.z - 0.1f);
				}
			}
			/* ---追加した部分ここまで--- */
		} else {
			// Rayがヒットしなかったら向いている方向にMaxDistance伸ばす
			_LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MaxDistance);
		}
	}
}

 このように書き換えると、以下のようにオブジェクトにレーザーポインターに当たった状態でトリガーボタン or タッチパッドボタンを押すとオブジェクトを大きくしたり、小さくしたりできます。レーザーポインターの先にオブジェクトが存在しない場合は、ボタンを押しても何も起こりません。


 以上、長々となってしまいましたが、Oculus Goコントローラーの表示方法とボタン取得方法でした。

 これでOculus Goコントローラーのボタンなどを取得できるようになったので、色々アプリでボタン操作を実装することができます。
 楽しみが広がりますね。


・参考資料