CrossRoad

XRを中心とした技術ブログ。 Check also "English" category.

Oculus Utilities for Unity とゲームパッドの使い方

ちょっと長いですが、Oculus Utilities for Unityを使ってゲームパッドでプレーヤー以外を操作する方法です。

[やりたいこと]

VR内のプレーヤー以外のオブジェクトを、ゲームパッドで動かしたい

一般的に、ゲームパッドを使うときは、自分自身を操作するのが普通です。しかし、ゲームパッドはボタンがたくさんあるので、プレーヤー以外も動かしたい、ということがありました。

そこで、方法を備忘録として書いておきます。なお、開発環境はUnity5.3.4p1、動作環境はGearVR(Galaxy S6)です。

1. 準備

私が変更した内容自体は大したことないのですが、OculusのSDKはアップデートが頻繁なので、他のバージョンでは動かない可能性があります。そこで、今回私が動作確認した開発環境の準備手順も残しておきます。

なお、OculusのHPによると、下記手順で書かれたバージョンの組み合わせでしか動かないので注意してください、だそうです。

1-1. Unity 5.3.4p1をインストール

ここから5.3.4p1を選んで、"ダウンロード(Win) "から入手します。

なお、p1とはパッチリリースの1回目という意味です。詳細は以前の記事をご参照ください。

【2017/8/22 Unity脆弱性対応方法追記】Unityのパッチを当てる方法 - CrossRoad

1-2. Oculus Plugin for Unity5 v1.3.0を導入

これはUnity5.3.4p1のOculusRift用ビルドのバイナリです。Oculus Utilities for Unity v1.3.0を使うために必須です。ここから入手します。

入手後、以下のようにして、バイナリを差し替えます。

1-2-1. Unityが開いていたら閉じる
1-2-2. エクスプローラでC:\Program Files\Unity\Editor\Data\VR\oculus に移動する
1-2-3. 上記の中にあるフォルダを全て削除する (oculusフォルダ以下を削除する)
1-2-4. OVRPlugin.zipに入っているフォルダを全てoculusフォルダにコピーする

1-3. Oculus Utilities for Unity5 v1.3.0をインポート

ここからダウンロードします。

Unityでプロジェクトを開き、ダウンロードしたunitypackageをインポートします。ここまでやって準備完了です。ちなみに、Oculus Plugin for Unity5を入れ忘れると、UnityEditorで実行時にUnityが落ちます。
(当初、手順をよく読んでおらず、そこではまりました。。)

また、この記事を書いている途中に、v1.3.0がv1.3.2に変わってしまいました。v1.3.0を入手する場合は、取得ページにて、カテゴリとバージョンを指定すれば取得できます。

2. スクリプトの作成、修正

2-1. コントロール用スクリプトの作成

動かしたいオブジェクト向けをコントロールするためのスクリプトを作ります。今回は、CubeController.csとしました。たとえば、以下のように書きます。

using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

public class Gamepad_Controller : MonoBehaviour {

    public GameObject model1Obj;
    private float speed = 0.1f;

    void Update() {
        /*コントローラの十字キーでオブジェクトを動かす場合*/
        if (OVRInput.Get(OVRInput.Button.DpadUp)){
            model1Obj.transform.position += model1Obj.transform.right * speed;
        }
        if (OVRInput.Get(OVRInput.Button.DpadDown)){
            model1Obj.transform.position -= model1Obj.transform.right * speed;
        }
        if (OVRInput.Get(OVRInput.Button.DpadLeft)){
            model1Obj.transform.position -= model1Obj.transform.forward * speed;
        }
        if (OVRInput.Get(OVRInput.Button.DpadRight)){
            model1Obj.transform.position += model1Obj.transform.forward * speed;
        } 
       /**/
    }
}

これをCubeにくっつけます。

2-2. OVRPlayerController.csの修正

Gamepadのどの部分を使うか次第ですが、もしPlayerの動作に割り当たっているボタンを別オブジェクトの操作に使いたい場合、OVRPlayerController.csから、該当部分をコメントアウトする必要があります。

下記は、OVRInput.Buttonでgrepした、Gamepadでキーが割当たっている部分です。必要に応じてコメントアウトしてください。

/*232行目付近 (実際のソースコードを抜粋していますが、インデントなど整形しています。以下同様です。)*/
/*コントローラの十字キー(上下のみ)*/
/*
if (OVRInput.Get(OVRInput.Button.DpadUp)){
	moveForward = true;
	dpad_move   = true;
}
if (OVRInput.Get(OVRInput.Button.DpadDown)){
	moveBack  = true;
	dpad_move = true;
}
*/
/*280行目付近*/
/*L!,R1ボタンによるプレーヤの回転*/
/*
bool curHatLeft = OVRInput.Get(OVRInput.Button.PrimaryShoulder);

if (curHatLeft && !prevHatLeft)
	euler.y -= RotationRatchet;

prevHatLeft = curHatLeft;
bool curHatRight = OVRInput.Get(OVRInput.Button.SecondaryShoulder);

if(curHatRight && !prevHatRight)
	euler.y += RotationRatchet;
        prevHatRight = curHatRight;
*/
/*380行目付近*/
/*左アナログスティックによる移動、右アナログスティックによる回転*/
/*
#if !UNITY_ANDROID // LeftTrigger not avail on Android game pad
	moveInfluence *= 1.0f + OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger);
#endif
  Vector2 primaryAxis = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick);
	if(primaryAxis.y > 0.0f)
            MoveThrottle += ort * (primaryAxis.y * transform.lossyScale.z * moveInfluence * Vector3.forward);

   if(primaryAxis.y < 0.0f)
            MoveThrottle += ort * (Mathf.Abs(primaryAxis.y) * transform.lossyScale.z * moveInfluence * BackAndSideDampen * Vector3.back);

	if(primaryAxis.x < 0.0f)
            MoveThrottle += ort * (Mathf.Abs(primaryAxis.x) * transform.lossyScale.x * moveInfluence * BackAndSideDampen * Vector3.left);

	if(primaryAxis.x > 0.0f)
            MoveThrottle += ort * (primaryAxis.x * transform.lossyScale.x * moveInfluence * BackAndSideDampen * Vector3.right);
	    Vector2 secondaryAxis = OVRInput.Get(OVRInput.Axis2D.SecondaryThumbstick);
	   euler.y += secondaryAxis.x * rotateInfluence;
*/

2-3. OVRPlayerController Prefabの導入

Hierarchy ViewにMain Cameraがある場合は削除し、OVRPlayerControllerのPrefabを入れます。

UnityのVR Supported機能があれば、Main Cameraを使ってもVRは使えますが、OVRPlayerController のPrefabを使わない場合、2-1で作ったスクリプトの動きがおかしくなることがありました。

これで準備は完了です。あとは、ゲームパッドを接続して実行すれば、OVRPlayerControllerではなくCubeを動かすことができます。

3. (参考)ゲームパッドとキーコードの関係

OVRInput.csを見ると、各キーコードが書かれています。

	public enum Button
	{
		None                      = 0,          ///< Maps to RawButton: [Gamepad, Touch, LTouch, RTouch: None]
		One                       = 0x00000001, ///< Maps to RawButton: [Gamepad, Touch, RTouch: A], [LTouch: X]
		Two                       = 0x00000002, ///< Maps to RawButton: [Gamepad, Touch, RTouch: B], [LTouch: Y]
		Three                     = 0x00000004, ///< Maps to RawButton: [Gamepad, Touch: X], [LTouch, RTouch: None]
		Four                      = 0x00000008, ///< Maps to RawButton: [Gamepad, Touch: Y], [LTouch, RTouch: None]
		Start                     = 0x00000100, ///< Maps to RawButton: [Gamepad: Start], [Touch, LTouch, RTouch: None]
		Back                      = 0x00000200, ///< Maps to RawButton: [Gamepad: Back], [Touch, LTouch, RTouch: None]
		PrimaryShoulder           = 0x00001000, ///< Maps to RawButton: [Gamepad: LShoulder], [Touch, LTouch, RTouch: None]
		PrimaryIndexTrigger       = 0x00002000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LIndexTrigger], [RTouch: RIndexTrigger]
		PrimaryHandTrigger        = 0x00004000, ///< Maps to RawButton: [Gamepad: None], [Touch, LTouch: LHandTrigger], [RTouch: RHandTrigger]
		PrimaryThumbstick         = 0x00008000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstick], [RTouch: RThumbstick]
		PrimaryThumbstickUp       = 0x00010000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickUp], [RTouch: RThumbstickUp]
		PrimaryThumbstickDown     = 0x00020000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickDown], [RTouch: RThumbstickDown]
		PrimaryThumbstickLeft     = 0x00040000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickLeft], [RTouch: RThumbstickLeft]
		PrimaryThumbstickRight    = 0x00080000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickRight], [RTouch: RThumbstickRight]
		SecondaryShoulder         = 0x00100000, ///< Maps to RawButton: [Gamepad: RShoulder], [Touch, LTouch, RTouch: None]
		SecondaryIndexTrigger     = 0x00200000, ///< Maps to RawButton: [Gamepad, Touch: RIndexTrigger], [LTouch, RTouch: None]
		SecondaryHandTrigger      = 0x00400000, ///< Maps to RawButton: [Gamepad: None], [Touch: RHandTrigger], [LTouch, RTouch: None]
		SecondaryThumbstick       = 0x00800000, ///< Maps to RawButton: [Gamepad, Touch: RThumbstick], [LTouch, RTouch: None]
		SecondaryThumbstickUp     = 0x01000000, ///< Maps to RawButton: [Gamepad, Touch: RThumbstickUp], [LTouch, RTouch: None]
		SecondaryThumbstickDown   = 0x02000000, ///< Maps to RawButton: [Gamepad, Touch: RThumbstickDown], [LTouch, RTouch: None]
		SecondaryThumbstickLeft   = 0x04000000, ///< Maps to RawButton: [Gamepad, Touch: RThumbstickLeft], [LTouch, RTouch: None]
		SecondaryThumbstickRight  = 0x08000000, ///< Maps to RawButton: [Gamepad, Touch: RThumbstickRight], [LTouch, RTouch: None]
		DpadUp                    = 0x00000010, ///< Maps to RawButton: [Gamepad: DpadUp], [Touch, LTouch, RTouch: None]
		DpadDown                  = 0x00000020, ///< Maps to RawButton: [Gamepad: DpadDown], [Touch, LTouch, RTouch: None]
		DpadLeft                  = 0x00000040, ///< Maps to RawButton: [Gamepad: DpadLeft], [Touch, LTouch, RTouch: None]
		DpadRight                 = 0x00000080, ///< Maps to RawButton: [Gamepad: DpadRight], [Touch, LTouch, RTouch: None]
		Up                        = 0x10000000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickUp], [RTouch: RThumbstickUp]
		Down                      = 0x20000000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickDown], [RTouch: RThumbstickDown]
		Left                      = 0x40000000, ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickLeft], [RTouch: RThumbstickLeft]
		Right     = unchecked((int)0x80000000), ///< Maps to RawButton: [Gamepad, Touch, LTouch: LThumbstickRight], [RTouch: RThumbstickRight]
		Any                       = ~None,      ///< Maps to RawButton: [Gamepad, Touch, LTouch, RTouch: Any]
	}

今回使ったゲームパッド「Stratus XL for Windows and Android」での代表的なキー割当は以下の通りです。

Stratus XL for Windows and Androidのキー割り当て 表

Stratus XL for Windows and Androidのキー割り当て 背面

キー割当は英語だとちょっとわかりづらいので、整理しておくと今後も役立ちそうですね。