Hiromuブログ

HoloLens などに関するブログです

MRTK のポインター

今回は以前の記事ではあまり詳細を触れなかったポインターについて。

具体的な実装の確認に入る前に、ポインターについて理解するには MRTK の入力システム(Input System)について理解する必要があります。入力システムは MRTK のアーキテクチャ全体の中の以下赤枠の部分になります。

f:id:HiromuKato:20200705182357p:plain

MRTK 入力システム

入力システムに関するドキュメントは以下。

ドキュメントにある以下の図が入力システムについて分かりやすくまとまっています。 f:id:HiromuKato:20200705182421p:plain

図を見ると入力システムは、以下のもので構成されていることがわかります。

  • Device Manager(Data Provider)

    • (プロファイルで設定している)MixedRealityInputSystem サービスによって生成される
    • 入力データプロバイダとも言い、コントローラーの検出や生成、ライフタイムの管理を行う
    • 下位レベルの API(Windows Mixed Reality API、OpenVR API など)とのインターフェースをとり、それらのシステムからのデータをMRTKの上位レベルの入力抽象化に適合するものに変換する
  • Controller

    • ゲームコントローラーなどのいわゆるコントローラーを表したもの(HoloLens 2 においては手もコントローラのひとつ)
    • デバイスマネージャーに生成される(たとえば、WindowsMixedReality デバイスマネージャーは、手を検出すると WindowsMixedRealityArticulatedHand コントローラーを生成してそのライフタイムを管理する)
    • コントローラーの Update ループでは、基盤となる API システムを呼び出す
    • コントローラー入力は、InputSystem プロファイル内の ControllerMapping プロファイルを介して入力アクションにマップされる
  • Pointer

    • デバイスマネージャーがポインターを生成する
    • 他のGameObjectと特定の方法でインタラクションする
    • 複数のポインターが存在する可能性があるので、Pointer Mediator がポインターの状態に基づいてどのポインターをアクティブにするか決定する
      • 例:ニアポインター(PokePointerやGrabPointer)がアクティブのときはコントローラーのすべてのファーポインターを無効にする
    • ポインターはコントローラーの参照を持つ
    • コントローラーのポインターは controller.InputSource.Pointers のように取得できる
    • 入力イベントはUIコンポーネントで直接処理できるが、実装をデバイスに依存しないようにするには、ポインターイベントを使用することが推奨される
  • Focus

    • ポインターのイベントは、フォーカスされているオブジェクトに送られる
  • Cursor

    • ポインターの操作に関する追加の視覚的手がかりを与えるポインターに関連付けられたエンティティ
  • Interactable

    • フォーカスの開始/終了、入力のダウン/アップなどの入力イベントをリッスンし、これらのイベントに応答して視覚的状態を更新するUXコンポーネント
  • GameObject with PointerHandler

    • 図の右上にありますが、Interactableはポインターとインタラクションをするために必須なものではなく、コライダーとIMixedRealityPointerHandler を実装したコンポーネントがあればインタラクション可能

以上のような階層で構成されているものの一つにポインターがあることが分かったので、ポインターについて詳しく見ていきます。

ポインター詳細

ポインターに関しては、以下2つのドキュメントに目を通すと良いと思います。

  • Architecture / Input System / Controllers, pointers, and focus

    • ポインターは、GameObject との対話に使用される
    • ポインターのカテゴリー
      • Far pointers
      • Near pointers
      • Teleport pointers
    • ポインターメディエーション
      • 単一のコントローラーが複数のポインターを持つことができるため、どのポインターをアクティブにするかを仲介するコンポーネント
    • ポインターは、Input System プロファイルの Pointers の項目で設定する
    • ポインターのライフサイクルは、一般的に次のとおり
      • デバイスマネージャーがコントローラーを検出し、コントローラーに関連付けられたポインターを生成する
      • FocusProviderは、Updateループで、すべての有効なポインターを反復処理し、関連するレイキャストまたはヒット検出ロジックを実行する。 これは、特定の各ポインターによってフォーカスされるオブジェクトを決定するために使用される
      • デバイスマネージャーは、コントローラーソースが失われたことを検出すると、失われたコントローラーに関連付けられているポインターを破棄する
  • Feature Overviews / Input System / Pointers

    • ポインターは、新しいコントローラーが検出されると、実行時に自動的にインスタンス化される
    • 1つのコントローラーに複数のポインターを接続できる
      • 多関節ハンドコントローラーは PokePointer、GrabPointer、DefaultControllerPointer(つまり、ハンドレイ)に関連付けられている
    • ポインタープロファイルは、カーソル、実行時に使用可能なポインターのタイプ、およびそれらのポインターが互いに通信して、アクティブにするポインターを決定する方法を決定する
    • プロファイルの Pointer Options で、ポインタが有効なコントローラとポインターのプレハブ、利き手を設定する
    • 以下のポインタがMRTKで用意されている(Assets/MRTK/SDK/Features/UX/Prefabs/Pointers の各プレハブにポインターコンポーネントがアタッチされている)
      f:id:HiromuKato:20200705182452p:plain
    • Far pointers
      • LinePointer : ベースポインタークラス
      • CurvePOinter : 曲線のポインター
      • ShellHandRayPointer : MRTKポインタープロファイルのデフォルトポインター
      • GGVPointer : HoloLens 1 スタイルのポインター
      • TouchPointer : Unityタッチ入力(つまり、タッチスクリーン)の操作を担当するポインター
      • MousePointer : タッチではなくマウスを利用したポインター
        • MRTKではデフォルトではマウスはサポートされていないが、入力プロファイルに MouseDeviceManager タイプの新しい入力データプロバイダを追加し、MixedRealityMouseInputProfileをデータプロバイダに割り当てることで有効にすることができる
    • Near pointers
      • PokePointer : NearInteractionTouchable(uGUIの場合は NearInteractionTouchableUnityUI)コンポーネントを持つ GameObject とタッチインタラクションをサポートするポインター
      • SpherePointer : NearInteractionGrabbable コンポーネントを持つ GameObject を掴む操作の入力に便利なポインター
    • Teleport pointers (VR向け機能)

      • TeleportPointer : ユーザーを移動するためのアクションが実行されると、テレポート要求が発生する
      • ParabolicTeleportPointer : ユーザーを移動させるために放物線のレイキャストでアクションが取られると、テレポート要求が発生する
    • Pointer event interfaces

      以下のインターフェイスを 1 つ以上実装し、コライダーを持つ GameObject に割り当てられた MonoBehaviour は、関連するインターフェイスで定義された Pointer インタラクションイベントを受信する

      イベント 説明 ハンドラー
      Before Focus Changed / Focus Changed ポインターがフォーカスを変更するたびに、フォーカスを失ったゲームオブジェクトと、フォーカスを獲得したゲームオブジェクトの両方に発生する IMixedRealityFocusChangedHandler
      Focus Enter / Exit 最初のポインタが入ったときにフォーカスを得るゲームオブジェクトと、最後のポインタが離れるときにフォーカスを失うゲームオブジェクトの上に発生する IMixedRealityFocusHandler
      Pointer Down / Dragged / Up / Clicked ポインターの押下、ドラッグ、リリースを報告するために発生する IMixedRealityPointerHandler
      Touch Started / Updated / Completed PokePointerなどのタッチ対応ポインターによって発生し、タッチアクティビティを報告する IMixedRealityTouchHandler
    • ポインタ入力イベント

      • ポインター入力イベントは、通常の入力イベントと同様の方法で MRTK 入力システムによって認識および処理される
      • 違いは、ポインター入力イベントは、入力イベントを発生させたポインターによってフォーカスされている GameObject と、グローバル入力ハンドラーによってのみ処理されること(通常の入力イベントは、すべてのアクティブなポインターに対してフォーカスされている GameObject によって処理される)
        1. MRTK入力システムは、入力イベントが発生したことを認識する
        2. MRTK入力システムは、登録されたすべてのグローバル入力ハンドラーへの入力イベントに関連するインターフェース関数を起動する
        3. 入力システムは、イベントを発生させたポインターに対してどの GameObject にフォーカスがあるかを決定する
          1. 入力システムは、Unityのイベントシステムを利用して、フォーカスされた GameObject のすべての一致するコンポーネントに関連するインターフェイス関数を起動する
          2. いずれかの時点で入力イベントが使用済みとマークされている場合、プロセスは終了し、それ以降のゲームオブジェクトはコールバックを受け取らない
            • 例:インターフェース IMixedRealityFocusHandler を実装するコンポーネントが検索され、GameObjectが取得またはフォーカスを失う
            • 注:現在の GameObject で目的のインターフェイスに一致するコンポーネントが見つからない場合、Unity イベントシステムはバブルアップして親 GameObject を検索します。
        4. グローバル入力ハンドラーが登録されておらず、一致するコンポーネント/インターフェースを持つ GameObject が見つからない場合、入力システムは、フォールバック登録された各入力ハンドラーを呼び出します
    • Example

    • すべての有効なポインタを調べる方法

var pointers = new HashSet<IMixedRealityPointer>
foreach (var inputSource in CoreServices.InputSystem.DetectedInputSources)
{
    foreach (var pointer in inputSource.Pointers)
    {
        if (pointer.IsInteractionEnabled && !pointers.Contains(pointer))
        {
            pointers.Add(pointer);
        }
    }
}
  • Primary pointer

    • 開発者はFocusProviders PrimaryPointerChangedイベントをサブスクライブして、フォーカスのあるプライマリポインターが変更されたときに通知を受けることができる
      • ユーザーが現在、視線やハンドレイなどの入力ソースを介してシーンと対話しているかどうかを識別するのに役立つ
    • PrimaryPointerExample (Assets/MRTK/Examples/Demos/Input/Scenes/PrimaryPointer) シーンでは、新しいプライマリポインタに応答するイベントにPriorimaryPointerChangedHandlerを使用する方法を示している
  • Pointer result

    • フォーカスのあるオブジェクトを決定するために使用されるシーンクエリの現在の結果
    • 視線入力、ハンドレイ等のレイキャストポインタの場合、レイキャストヒットの位置と法線が含まれる
    • PointerResultExampleシーン(Assets/MRTK/Examples/Demos/Input/Scenes/PointerResult/PointerResultExample.unity)は、ポインタResultを使ってヒットした場所にオブジェクトをスポーンする方法を示している
  • Disable pointers

    • ポインタを有効にしたり無効にしたりするには(例えば、ハンドレイを無効にするには)、PointerUtils を介して、指定されたポインタタイプの PointerBehavior を設定する
  • UnityEditor を介したポインターインタラクション

    • IMixedRealityPointerHandler によって処理されるポインタイベントについては、MRTK では PointerHandler コンポーネントの形でさらに便利な機能を提供しており、Unity Events を介してポインタイベントを直接処理することができる
  • ポインターの長さ

    • ファーポインターには、レイキャストやシーン内の他のオブジェクトとのインタラクションを制限する設定がある
    • デフォルトでは、この値は10メートルに設定されている(HoloLens シェルの動作との整合性を保つために選択された値)
    • この値は、DefaultControllerPointer プレハブの ShellHandRayPointer コンポーネントのフィールドを更新することで変更できる

まとめ

ほとんどドキュメントを翻訳しただけのような内容になりましたが、入力システムとポインターについて確認しました。

簡単にまとめるとポインターとは、

  • デバイスマネージャーに管理され
  • コントローラーに関連付けられたもので
  • オブジェクトとインタラクションするために必要なもの

という感じでしょうか。 その点を踏まえつつ、HoloLens 2 におけるハンド操作部分だけを抜き出した絵にしてみました。 f:id:HiromuKato:20200705182518p:plain

次回(?)は GazePointer によって生成される InternalGazePointer について確認していこうと思います。