ハンド メニュー - MRTK3

Hand Menu

ハンド メニューを使用すると、ユーザーは頻繁に使用される関数に対して手動でアタッチされた UI を表示できます。 これらは通常、クイック アクションを提供する小さな ボタン グループ です。 ただし、情報や設定を表示するためのより複雑なレイアウトがユーザーにハンド メニューとして提供される場合があり、多くの場合、手からメニューを "引き裂く" オプションを使用して、世界に固定することができます。

[ハンド] メニューには、[フラット ハンドが必要] オプションと [視線アクティブ化を使用する] オプションが用意されており、他のオブジェクトとの対話中に誤ったアクティブ化が行われないようにします。 不要なアクティブ化を防ぐために、これらのオプションを使用することをお勧めします。

シーンとプレハブの例

Note

現在、ハンド メニュー プレハブは、キャンバス以外のコントロールを使用して構築されています。 間もなく、Button グループのドキュメントで詳しく説明されているように、Canvas コンポーネントと自動レイアウト システムを使用して再構築されます。

テンプレート プロジェクトを使用している場合は、 HandMenuExamples.unity すべてスクリプトを使用して、ハンド メニューのいくつかの一般的な構成を HandConstraintPalmUp 示します。

Hand Menu Example Scene

HandMenu1x4 と HandMenu2x4

OnFirstHandDetected() イベントと OnLastHandLost() イベントの MenuContent オブジェクトをアクティブ化および非アクティブ化することで、これらのプレハブでメニューが表示および非表示になります。 この動作は、短い操作時間が必要な UI が小さいメニューに推奨されます。

Hand Menu Example 2x4

HandMenuLarge

このプレハブは、長い対話時間を必要とする大規模または複雑な UI の例を示しています。 この種類の UI では、使いやすさを向上させ、腕の疲労を避けるために、手動ドロップでメニューをワールド ロックすることをお勧めします。 この例では、メニューをワールド ロックする 'グラブアンドプル' もサポートしています。

この例では、 OnFirstHandDetected() イベントで MenuContent オブジェクトをアクティブにすることで、メニューが表示および非表示になります。 OnLastHandLost() イベントを使用すると、閉じるボタンがアクティブになり、配置アニメーションがトリガーされます。 アニメーションは単純なスケーリングの変動です。 OnLastHandLost() イベントで MenuContent を非表示にしていないため、手が表示されないと、メニューは自動的にワールド ロックされます。 Palm Up セクションの値は、手のドロップでドラッグしすぎずにメニューをワールド ロックするように最適化されています。

Hand Menu Example Large 1

Palm Up configuration

次の使用例は、メニューの下部領域にグラブ可能なバーと自動ワールド ロック動作を提供します。 ユーザーは、これをつかむことで、メニューを手から明示的に切り離して世界に配置できます。 これを実現するには、ObjectManipulator の ManipulationStarted() イベントで SolverHandler.UpdateSolvers を無効にします。 それ以外の場合、HandConstraint ソルバーはメニューを手の位置の近くに配置しようとするため、メニューをデタッチできません。 また、HandConstraintPalmUp.StartWorldLockReattachCheckCoroutine を使用して、ユーザーが手を上げてメニューを手に再アタッチできるようにします。

Hand Menu Example Large 2

最後に、閉じるボタンは、HandConstraint ソルバーの機能を復元するために SolverHandler.UpdateSolvers を再アクティブ化する必要があります。

Hand Menu Example Large 3

スクリプト

この HandConstraint の動作では、手で制約されるコンテンツ (ハンド UI、メニューなど) で追跡対象のオブジェクトを安全な領域に制約するソルバーが提供されます。安全な領域は、手と交差しない領域と見なされます。 手のひらがユーザーの方を向いているときに、ソルバーの追跡対象のオブジェクトをアクティブ化する一般的な動作を示すために、HandConstraintPalmUp と呼ばれる HandConstraint の派生クラスも含まれています。

その他のドキュメントについては、各 HandConstraint プロパティで使用できるツールヒントを参照してください。 いくつかのプロパティについては、以下で詳しく説明します。

  • [セーフ ゾーン]: セーフ ゾーンは、手の上でコンテンツを拘束する場所を指定します。 手との重複や相互作用の質の向上を避けるために、コンテンツをウルナー側に配置することをお勧めします。 セーフゾーンは、カメラのビューに直交する平面に投影された手の向きと、手の周囲の境界ボックスに対するレイキャストによって計算されます。 セーフ ゾーンは、操作XRNodeするように定義されています。 各セーフ ゾーンが異なるコントローラーの種類で何を表しているのかを確認することをお勧めします。

  • [手を向けるまで]カメラに従う このアクティブにすると、カメラに向いているときにメニューが視線に十分に合うまで、ソルバーは手の回転に従います。 この作業を行うには、ソルバーのHandConstraintSolver角度が変化するLookAtMainCameraにつれて GazeAlignmentLookAtTrackedObjectの範囲を変更SolverRotationBehaviorします。

Hand Menu Example Safe Zone

  • アクティブ化イベント: 現在、 HandConstraint 4 つのアクティブ化イベントがトリガーされます。 これらのイベントは、さまざまな組み合わせで使用して、一意 HandConstraint の動作を作成できます。

    • OnHandActivate: 手が IsHandActive メソッドを満たしたときにトリガーします。
    • OnHandDeactivate: IsHandActive メソッドを満たされなくなったときにトリガーします。
    • OnFirstHandDetected: ハンド 追跡状態がビュー内のハンドからビューの最初のハンドに変わると発生します。
    • OnLastHandLost: ハンド 追跡状態が、ビュー内の少なくとも 1 つのハンドからビュー内のハンドに変わると発生します。
  • ソルバーのアクティブ化/非アクティブ化ロジック: 現在、ロジックをアクティブ化および非アクティブ化HandConstraintPalmUpするための推奨事項は、オブジェクトを無効または有効にするのではなく、's UpdateSolver 値を使用SolverHandlerして行うことです。 これは、アタッチされたメニューの ManipulationHandler "OnManipulationStarted/Ended" イベントの後にトリガーされるエディター ベースのフックにより、サンプル シーンで確認できます。

    • 手動制約ロジックの停止: ハンド制約オブジェクトを停止するように設定する (アクティブ化/非アクティブ化ロジックを実行しない) 場合は、HandConstraintPalmUp を無効にするのではなく、UpdateSolver を False に設定します。
      • 視線入力ベースの (または視線参照ベースではない) 再アタッチ ロジックを有効にする場合は、その後に関数を HandConstraintPalmUp.StartWorldLockReattachCheckCoroutine() 呼び出します。 これにより、"IsValidController" 条件が満たされているかどうかを確認し続けるコルーチンがトリガーされ、更新された後 (またはオブジェクトが無効になった場合) に UpdateSolver が True に設定されます。
    • ハンド制約ロジックの開始: ハンドの制約付きオブジェクトを設定して、(アクティブ化条件を満たしているかどうかに基づいて) ハンドのフォローを再開する場合は、SolverHandler の UpdateSolver を true に設定します。
  • ロジックの再アタッチ: 現在、HandConstraintPalmUp's UpdateSolver が True かどうかに関係なく、追跡対象のポイントにターゲット オブジェクトを自動的にSolverHandler再アタッチできます。 これは、ワールド ロックされた後に 's StartWorldLockReattachCheckCoroutine() 関数を呼び出HandConstraintPalmUpすことによって行われます (この場合、SolverHandler の UpdateSolver を実質的に False に設定しています)。