見出し画像

XR Interaction ToolkitとInput System

これまでの記事では、XR Interaction Toolkitでコントローラーを使用する場合、Action-basedのInput Systemを使用し、サンプルで用意されているアクションのマッピング(ActionMap)を適用してきました。
Input Systemは、ActionMapを用意しなくても使用できるので、ActionMapを使用しない方法と独自のActionMapを作成して使用する方法で、ハンドコントローラーからの入力を処理してみます。

環境

Unityバージョン:2021.1.14f1
XR Interaction Toolkit:1.0.0-pre.4
Oculus Quest2:v30 ソフトウェア (※)

※Oculus Quest2のシステムが、v30にバージョンされた事により、テスト機能「VRにデスクを持ち込もう」を有効にして、同記事(過去記事含む)のプログラムを実行しようとすると、対応していない旨のエラーが表示されるようになりました。
(対応させる方法が知りたい…)

プロジェクトの作成及び設定

以前の記事「XR Interaction Toolkitを使用してみる」の「プロジェクトの作成」と「プロジェクトの設定」を参考にしてください。
ここでは、Input Actionのサンプルのインポートは、実施しません。

画像1

プログラムの作成

Input Actionを直接指定してコントローラーの押下イベントを処理する方法とActionMapを作成してActionMapに応じたイベントを処理する方法を記載します。イベントに対する処理としてコントローラーを振動させます。
グリップボタンとトリガーボタンの押下で振動するようにします。

■VR用カメラの作成
VR用のカメラは、以前の記事「XR Interaction Toolkitで指定した物を掴む - プログラムの作成」の「VR用カメラの作成」を参考にしてください。

画像2

■HandControllerオブジェクトの無効化
今回は、コントローラーを使用して仮想空間上の物体に対する操作を行わないので、左右のコントローラーオブジェクトを無効化します。
HierarchyビューのXR Rig > Camera Offset > LeftHand Controllerを選択し、Inspectorビューに表示されているオプジェクト名の左にあるチェックボックスのチェックを外します。RightHand Controllerも同様にします。

画像3

■Input Actionを直接指定する方法
コントローラーのグリップからの入力とコントローラーに対する振動をInputActionとして指定するスクリプトを作成し、空のゲームオブジェクトに設定、InputActionにコントローラーの入力を割り当てます。

[InputActionを指定するスクリプトの作成]
指定されたInputActionに対する処理を記述するスクリプトを作成します。
ProjectビューのAssetsで右クリックして表示されるコンテキストメニューからCreate > C# Scriptを選択します。作成されたスクリプトの名称を「InputActionDirect」に変更します。

画像4

作成したInputActionDirectスクリプトを開いて以下の内容を記載します。

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.XR;

public class InputActionDirect : MonoBehaviour
{
   // InputActionを外部から設定
   [Header("Hand Controller Settings")]
   [SerializeField] private InputAction leftAction; // 左用の入力アクション
   [SerializeField] private InputAction rightAction; // 右用の入力アクション
   [SerializeField] private InputAction leftHaptic; // 左用の振動アクション
   [SerializeField] private InputAction rightHaptic; // 右用の振動アクション

   // コンポーネントが有効化された場合
   private void OnEnable()
   {
       leftAction.Enable();
       rightAction.Enable();
       leftHaptic.Enable();
       rightHaptic.Enable();
   }

   // コンポーネントが無効化された場合
   private void OnDisable()
   {
       leftAction.Disable();
       rightAction.Disable();
       leftHaptic.Disable();
       rightHaptic.Disable();
   }

   // インスタンス化直後に実行
   private void Awake()
   {
       // 入力が発生した時の処理
       leftAction.performed += context =>
       {
           // 振動用のアクションが存在する場合、振動させる
           if (leftHaptic.activeControl?.device is XRControllerWithRumble rumbleController)
           {
               rumbleController.SendImpulse(0.9f,0.75f);
           }
       };
       // 入力が発生した時の処理
       rightAction.performed += context =>
       {
           // 振動用のアクションが存在する場合、振動させる
           if (rightHaptic.activeControl?.device is XRControllerWithRumble rumbleController)
           {
               rumbleController.SendImpulse(0.9f,0.75f);
           }
       };
   }
}

[空のゲームオブジェクトに設定]
空のゲームオブジェクトを作成、上記で作成したInputActionDirectスクリプトをコンポーネントとして設定します。
Hierarchyビューを右クリックしコンテキストメニューよりCreate Emptyを選択します。作成されたGameObjectの名称を「InputActionController」に変更します。
作成したInputActionControllerを選択し、Inspectorビューの「Add Component」から作成したInput Action Directを追加します。

画像5

[スクリプトにコントローラーの入力を割り当て]
追加したInputActionDirectスクリプトに、コントローラーの入力を割り当てます。
InputActionDirectのHand Controller Setting - Left Actionの設定ボタン(歯車の形のボタン)を押下し、Action Typeを「Button」に変更します。追加ボタン(+のボタン)を押下し「Add Binding」を選択します。

Unity_InputActionController_注釈

「<No Binding>」と表示された1行が追加されるので、これをダブルクリックし設定ダイアログを表示します。設定ダイアログのPathをクリックし、XR Controller > XR Controller (LeftHand) > Optional Controls > gripPressed を選択します。
同様にRight Actionも設定します。

Unity_InputActionController_1行追加_注釈

振動用のActionであるLeft HapticとRight Hapticも同様に設定しますが、設定ボタンでの設定は、初期値(Action Type:Value、Control Type:Any)のまま変更しません。追加ボタンでのBindingは、Pathの指定をTボタンを押下しテキスト入力可能とし、Left Hapticは、「<XRController>{LeftHand}/*」、Right Hapticは、「<XRController>{RightHand}/*」を入力します。

Unity_InputAction_Binding_Path_Text_注釈

設定後は、以下のようになります。

画像9

■ActionMapを使用する方法
コントローラーの入力とプログラム内で使用する論理的な名称の紐付けを定義したActionMapを作成します。今回は、トリガーボタンと振動に対して定義します。その後、定義した名称に対応した処理を記述したスクリプトを作成、空のゲームオブジェクトに設定します。
ActionMapで定義したアクション名に対応した処理をスクリプトでイベントとして受け取るため必要なPlayer Inputコンポーネントを追加、設定する事でスクリプトに記述した内容を処理する事が可能になります。

[ActionMapの作成]
コントローラーのボタンと名称の定義をActionMapとして定義します。
ProjectビューのAssetsで右クリックして表示されるコンテキストメニューからCreate > Input Actionsを選択します。New Controlsが作成されるので、名称を「HapticActions」に変更します。

画像11

作成したHapticActionsをダブルクリックすると作成ウィンドウが表示されます。この作成ウィンドウは、ActionMapとActionの組み合わせを複数登録する事ができます。今回は、1つのActionMapしか使用しないので、1つだけ定義します。
Action Mapsの追加ボタンを押下し、追加された行の名称を「HapticMap」に変更します。
追加したHapticMapを選択し、Actionsに名称と対応するコントローラーの入力アクションを定義します。以下の組み合わせを定義していきます。

Action名:LeftAction、Path:<XRController>{LeftHand}/triggerPressed
Action名:RightAction、Path:<XRController>{RightHand}/triggerPressed
Action名:LeftHaptic、Path:<XRController>{LeftHand}/*
Action名:RightHaptic、Path:<XRController>{RightHand}/*

アクションの設定内容は、上述の「Input Actionを直接指定する方法」と変わらないですが、Actino Typeの初期値が「Button」になっていますので、LeftHapticとRightHapticの場合は、Action Type:Value、Control Type:Anyに変更する必要があります。

画像10

[スクリプトの作成]
ActionMapに定義したAction名の入力が発生した時の処理をスクリプトに記述します。
ProjectビューのAssetsで右クリックして表示されるコンテキストメニューからCreate > C# Scriptを選択します。作成されたスクリプトの名称を「InputActionMap」に変更します。

画像12

作成したInputActionMapスクリプトを開いて以下の内容を記載します。

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.XR;

//必須コンポーネントとしてPlayerInputを指定
[RequireComponent(typeof(PlayerInput))]
public class InputActionMap : MonoBehaviour
{
   // ActionMapに定義されているActionに対応した処理
   // メソッド名を「"On" + ActionMapに定義したAction名」にする
   private void OnLeftAction(InputValue arg)
   {
       //振動させるデバイスを取得
       PlayerInput input = GetComponent<PlayerInput>();
       InputAction haptic = input.actions["LeftHaptic"];
       if (haptic.activeControl?.device is XRControllerWithRumble rumbleController)
       {
           rumbleController.SendImpulse(0.9f,0.25f);
       }
   }
   // ActionMapに定義されているActionに対応した処理
   private void OnRightAction(InputValue arg)
   {
       //振動させるデバイスを取得
       PlayerInput input = GetComponent<PlayerInput>();
       InputAction haptic = input.actions["RightHaptic"];
       if (haptic.activeControl?.device is XRControllerWithRumble rumbleController)
       {
           rumbleController.SendImpulse(0.9f,0.25f);
       }
   }
}

[空のゲームオブジェクトに設定]
空のゲームオブジェクトを作成、上記で作成したInputActionMapスクリプトをコンポーネントとして設定します。
Hierarchyビューを右クリックしコンテキストメニューよりCreate Emptyを選択します。作成されたGameObjectの名称を「InputActionMapController」に変更します。
作成したInputActionMapControllerを選択し、Inspectorビューの「Add Component」から作成したInput Action Mapを追加します。
追加すると必須コンポーネントとして指定したPlayer Inputが自動的に追加されます。

画像13

[Player Input コンポーネントの設定]
自動的に追加されたPlayer Inputコンポーネントに、作成したActionMapを指定します。
HierarchyビューからInputActionMapControllerを選択し、Inspectorビューに表示されたPlayer InputコンポーネントのActionsに作成したHapticActionsを指定します。DefaultMapとして「HapticMap」が初期値として設定されます。

画像14

実行

保存(File > Save)してから、Oculus Quest2が接続されている事を確認し、Build And Run(File > Build And Run)を実行します。
コンパイルが開始され、コンパイル終了後に接続されているOculus Quest2にプログラムが自動転送され実行されます。
LeftHand ControllerとRightHand Controllerを無効化しているため、赤い線は、表示されません。
トリガーボタンとグリップボタンの押下でそれぞれのコントローラーが振動し、トリガーボタンは短い振動、グリップボタンは長い振動になります。

参考

■公式APIリファレンス
InputAction
XRControllerWithRumble.SendImpulse

終わりに

XR Interaction ToolkitのInteractor / Interacableを使わないで、Input Systemを使用してハンドコントローラーの入力を処理しました。
実際は、コントローラーの入力を通して、仮想空間上の何かに対して影響を与える事(表示する、選択する、掴む、投げるなど)が多いので、Interactor / Interacableを使用した方が簡単に対応できますが、Input Systemの基本的な使用方法も知っておいた方が応用できる幅が広がります。

将来の自分への備忘録、ついでに誰かの何かのきっかけになれば幸いです。

この記事が気に入ったらサポートをしてみませんか?