コントローラー、ポインター、フォーカス — MRTK2
コントローラー、ポインター、フォーカスは、核となる入力システムによって構築された基礎の上に成り立つ高いレベルの概念です。 シーン内のオブジェクトを対話的に操作するためのメカニズムの大部分は、それらが合わさって実現されています。
Controllers
コントローラーは、物理コントローラー (6 Degrees of Freedom、多関節ハンドなど) を表現したものです。 デバイス マネージャーによって作成され、対応する下層システムと通信し、そのデータを MRTK 形式のデータとイベントに変換する役割を担います。
たとえば、Windows Mixed Reality プラットフォームの WindowsMixedRealityArticulatedHand
は、下層の Windows ハンド トラッキング API と連携して、手の関節やポーズなどのプロパティに関する情報を取得する役割を果たすコントローラーです。 そのデータを関連する MRTK イベントに変換 (RaisePoseInputChanged や RaiseHandJointsUpdated を呼び出すなど) し、その固有の内部状態を更新することで、TryGetJointPose
のクエリから正しいデータが返されるようにする役割を担っています。
一般に、コントローラーのライフサイクルでは、次のことが行われます。
新しいソースが検出されるとすぐにデバイス マネージャーによってコントローラーが作成されます (たとえば、デバイス マネージャーが手を検出して、トラッキングを開始します)。
コントローラーの Update() ループで、下層の API システムを呼び出します。
同じ Update ループで、コアの入力システム自体を直接呼び出すことで、入力イベントの変化を発生させます (HandMeshUpdated や HandJointsUpdated を発生させるなど)。
ポインターとフォーカス
ポインターは、ゲーム オブジェクトとの対話に使用されます。 このセクションでは、ポインターがどのように作成されて、どのように更新されるか、また、フォーカスのあるオブジェクトがどのように判断されるかについて説明します。 さらに、存在する各種のポインターとそれらがアクティブになるシナリオについても説明します。
ポインターのカテゴリ
ポインターは、通常、次のいずれかのカテゴリに分けられます。
遠距離ポインター
このタイプのポインターは、ユーザーから遠く離れたオブジェクトを対話的に操作するときに使用されます ("遠く離れた" とは単に "近くない" という意味です)。 このタイプのポインターは、通常、ワールドの遠くまで届く光線を打ち、ユーザーは、その光線を使って、自分のすぐ近くにはないオブジェクトと対話したり操作したりすることができます。
近距離ポインター
このタイプのポインターは、つかんだり触れたり操ったりできるほどユーザーに近い位置にあるオブジェクトを対話的に操作するときに使用されます。 一般に、このタイプのポインターは、近距離からオブジェクトを探すことによってオブジェクトと対話します (短距離へのレイキャスティング、球状のキャスティングによる近距離内のオブジェクトの検索、つかんだり触れたりすることができると考えられるオブジェクトの列挙のいずれかによってこれは行われます)。
テレポート ポインター
このタイプのポインターは、テレポーテーション システムに接続して、ポインターによって指定された場所にユーザーを移動させます。
ポインターの仲介
1 つのコントローラーが複数のポインターを備えていることもあるため (たとえば多関節ハンドは、近距離と遠距離の両方の対話ポインターを備えている場合があります)、どのポインターをアクティブにするかを調整するコンポーネントが存在します。
たとえば、ユーザーの手が押しボタンに近づいたら、ShellHandRayPointer
は表示を中止して、PokePointer
を機能させる必要があります。
それを管理するのが DefaultPointerMediator
です。すべてのポインターの状態に基づいて、アクティブにするポインターを決定する役割を担います。 その重要な役割の 1 つが、近距離ポインターがオブジェクトに近づいたときに、遠距離ポインターを無効にすることです (DefaultPointerMediator
を参照)。
ポインター メディエーターには、ポインターのプロファイルの PointerMediator
プロパティを変更することで別の実装を指定できます。
ポインターを無効にする方法
ポインター メディエーターはフレームごとに実行されるため、結局は、すべてのポインターのアクティブ状態と非アクティブ状態が、ポインター メディエーターによって制御されることになります。 したがって、ポインターの IsInteractionEnabled プロパティをコード内に設定しても、ポインター メディエーターによってフレームごとに上書きされます。 代わりに、PointerBehavior
を指定すれば、ポインターのオンとオフを自分で制御できます。 これが機能するのは、MRTK の既定の FocusProvider
と DefaultPointerMediator
を使用している場合のみであることに注意してください。
例: MRTK のハンド レイを無効にする
次のコードは、MRTK のハンド レイをオフにします。
// Turn off all hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff);
// Turn off hand rays for the right hand only
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff, Handedness.Right);
次のコードは、ハンド レイを MRTK の既定の動作に戻します。
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.Default);
次のコードは、つかむことのできるオブジェクトが近くにあるかどうかにかかわらず、ハンド レイを強制的にオンにします。
// Turn off all hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOn);
その他の例については、PointerUtils
と TurnPointersOnOff
に関するページを参照してください。
FocusProvider
FocusProvider
は、すべてのポインターの一覧を反復処理し、ポインターごとにフォーカスするオブジェクトが何かを確認する役割を担います。
それぞれの Update()
呼び出しで、次のことが行われます。
ポインター自体によって構成されたレイキャスティングとヒット検出を行うことで、すべてのポインターを更新します (たとえば、球体ポインターで SphereOverlap raycastMode が指定されると、FocusProvider によって行われるコリジョンは球体ベースとなります)。
フォーカスされているオブジェクトをポインターごとに更新します (つまり、オブジェクトがフォーカスを取得すると、それらのオブジェクトに対するイベントがトリガーされ、オブジェクトがフォーカスを失うと、focus lost がトリガーされます)。
ポインターの構成とライフサイクル
ポインターの構成は、入力システム プロファイルの [ポインター] セクションで行えます。
通常、ポインターのライフタイムは次のとおりです。
デバイス マネージャーがコントローラーの存在を検出します。 次に、このデバイス マネージャーが、
RequestPointers
の呼び出しを通じて、コントローラーに関連付けられた一連のポインターを作成します。FocusProvider がその Update() ループの中で、有効なポインターをすべて反復処理し、関連付けられているレイキャストを行うか、ヒット検出ロジックを実行します。 これを用いて、特定のポインターごとにフォーカスされているオブジェクトが特定されます。
- 入力の複数のソースが同時にアクティブになる可能性がある (2 つの手がアクティブな状態で存在するなど) ため、複数のオブジェクトが同時にフォーカスを受け取ることも考えられます。
デバイス マネージャーは、コントローラーのソースが失われたことを検出すると、その失われたコントローラーに関連付けられているポインターを破棄します。