ソルバーの概要 — MRTK2
ソルバーは、定義済みのアルゴリズムに従って、オブジェクトの位置と方向を計算する手段を容易にするコンポーネントです。 たとえば、ユーザーの視線入力レイキャストが現在ヒットしているサーフェスにオブジェクトを配置します。
さらに、コンポーネントの更新順序を Unity に指定する信頼できる方法がないため、ソルバー システムでは、これらの変換計算の操作の順序を確定的に定義します。
ソルバーでは、オブジェクトを他のオブジェクトまたはシステムにアタッチするためのさまざまな動作が提供されます。 もう 1 つの例は、(カメラに基づいて) ユーザーの前に浮遊するタグに沿ったオブジェクトです。 ソルバーをコントローラーとオブジェクトにアタッチして、オブジェクトをタグに沿ったコントローラーにすることもできます。 すべてのソルバーを安全にスタックすることができます。たとえば、タグに沿った動作 + 表面磁性 + 勢いです。
ソルバーの使用方法
ソルバー システムは、次の 3 つのカテゴリのスクリプトで構成されます。
Solver
: すべてのソルバーの派生元である基本抽象クラス。 状態の追跡、パラメーターと実装のスムージング、自動的なソルバー システムの統合、更新順序が提供されます。SolverHandler
: 追跡する参照オブジェクト (メイン カメラの変換、ハンド レイなど) を設定して、ソルバー コンポーネントの収集を処理し、適切な順序でそれらの更新を実行します。
3 番目のカテゴリはソルバー自体です。 次のソルバーでは、基本動作のための構成要素が提供されます。
Orbital
: 参照されるオブジェクトから指定された位置およびオフセットにロックします。ConstantViewSize
: 参照されるオブジェクトのビューに対して一定のサイズが維持されるようにスケーリングします。RadialView
: 参照されるオブジェクトによってキャストされたビュー コーン内のオブジェクトを保持します。Follow
: 参照されるオブジェクトの一連のユーザー定義の境界内にオブジェクトを保持します。InBetween
: 2 つの追跡対象オブジェクトの間にオブジェクトを保持します。SurfaceMagnetism
: ワールド内のサーフェスにレイをキャストし、そのサーフェスにオブジェクトを合わせます。DirectionalIndicator
: オブジェクトの位置と方向を方向インジケーターとして決定します。 SolverHandler の追跡対象の参照ポイントから、このインジケーターは指定された DirectionalTarget の方法を向きます。Momentum
: 他のソルバー/コンポーネントによって移動されるオブジェクトの勢いと弾力性をシミュレートするために、加速/ベロシティ/摩擦を適用します。HandConstraint
: GameObject と手が交差しない領域で、オブジェクトが手に従うように制約します。 メニューなどの手に制約される対話型コンテンツで役に立ちます。このソルバーは IMixedRealityHand で使用することが意図されていますが、IMixedRealityController でも動作します。HandConstraintPalmUp
: HandConstraint から派生しますが、アクティブにされる前に、手のひらがユーザーの方を向いているかどうかをテストするロジックが含まれています。 このソルバーは IMixedRealityHand コントローラーでのみ機能します。他のコントローラーの種類では、このソルバーはその基本クラスと同様に動作します。
ソルバー システムを使用するには、上記のいずれかのコンポーネントを GameObject に単純に追加します。 すべてのソルバーで SolverHandler
が必要となるため、Unity によって自動的に作成されます。
Note
ソルバー システムの使用方法の例については、SolverExamples.scene ファイルを参照してください。
追跡参照を変更する方法
SolverHandler
コンポーネントの [追跡対象の種類] プロパティでは、すべてのソルバーでアルゴリズムを計算するために使用される参照ポイントを定義します。 たとえば、単純な SurfaceMagnetism
コンポーネントを使用する Head
の値の種類は、ヘッドからのレイキャストと、サーフェスがヒットしたものを解決するためのユーザーの視線入力の方向になります。 TrackedTargetType
プロパティの有効な値を次に示します。
- Head : 参照のポイントは、メイン カメラの変換です
- ControllerRay: 基準点は、ライン レイの方向を指すコントローラー (つまり、モーション コントローラーまたはハンド コントローラー上のポインターの原点) 上の
LinePointer
変換です。- 利き手設定 (つまり、Left、Right、Both) を選択するには、
TrackedHandedness
プロパティを使用します
- 利き手設定 (つまり、Left、Right、Both) を選択するには、
- HandJoint: 参照ポイントは、特定のハンド ジョイントの変換です
- 利き手設定 (つまり、Left、Right、Both) を選択するには、
TrackedHandedness
プロパティを使用します - 使用するジョイント変換を決定するには、
TrackedHandJoint
プロパティを使用します
- 利き手設定 (つまり、Left、Right、Both) を選択するには、
- CustomOverride: 割り当てられた
TransformOverride
からの参照のポイント
Note
種類が ControllerRay と HandJoint の場合、ソルバー ハンドラーでは最初に左コントローラー/ハンド変換を提供することが試みられます。次に、前者が使用できない場合、TrackedHandedness
プロパティに他のものが指定されている場合を除き、右が提供されます。
各 TrackedTargetType に関連付けられているさまざまなプロパティの例
重要
ほとんどのソルバーでは、SolverHandler
によって提供される追跡対象の変換ターゲットの前方ベクトルが使用されます。 追跡対象の種類 [ハンド ジョイント] を使用する場合、パーム ジョイントの前方ベクトルは、手のひらを介さずに指を介してポイントされることがあります。 これは、ハンド ジョイントのデータを提供するプラットフォームに依存します。 入力シミュレーションと Windows Mixed Reality の場合、これは手のひらを介してポイントする "アップ ベクトル" です (つまり、緑のベクトルが上に、青いベクトルが前方に向かっています)。
これを回避するには、SolverHandler
の [追加の回転] プロパティを <90、0、0> に更新します。 これにより、ソルバーに渡される前方ベクトルが、手のひらを介して手の外にポイントされます。
または、追跡対象の種類 [コントローラー レイ] を使用して、両手でポイントする場合と同様の動作を取得します。
ソルバーを連結する方法
同じ GameObject に複数の Solver
コンポーネントを追加して、それらのアルゴリズムを連結することができます。 SolverHandler
コンポーネントによって、同じ GameObject に対するすべてのソルバーの更新が処理されます。 既定では、起動時に SolverHandler
によって GetComponents<Solver>()
が呼び出され、インスペクターに表示される順序でソルバーが返されます。
さらに、Updated Linked Transform プロパティを true に設定すると、計算された位置、向き、スケールを、すべてのソルバー (つまりGoalPosition
) がアクセスできる中間変数に保存するSolver
が指示されます。 false の場合、Solver
によって GameObject の変換が直接更新されます。 変換プロパティを中間の場所に保存することにより、他のソルバーで中間変数から計算を実行できます。 これは、Unity では gameObject.transform への更新を同じフレーム内にスタックできないためです。
Note
開発者は、SolverHandler.Solvers
プロパティを直接設定することによって、ソルバーの実行順序を変更できます。
新しいソルバーを作成する方法
すべてのソルバーは、抽象基本クラス (Solver
) から継承される必要があり ます。 ソルバー拡張機能の主な要件には、SolverUpdate
メソッドをオーバーライドすることが含まれます。 このメソッドでは、開発者は継承された GoalPosition
、GoalRotation
、GoalScale
プロパティを目的の値に更新する必要があります。 また、一般に、コンシューマーが必要とする参照のフレームとして SolverHandler.TransformTarget
を利用すると役立ちます。
以下に示すコードは、アタッチされたオブジェクトを SolverHandler.TransformTarget
の 2m 前に配置する InFront
という新しいソルバー コンポーネントの例を示しています。 コンシューマーによって SolverHandler.TrackedTargetType
が Head
として設定されている場合、SolverHandler.TransformTarget
はカメラの変換になります。したがって、このソルバーによって、接続されている GameObject が、ユーザーの視線入力のすべてのフレームの 2m 前に配置されます。
/// <summary>
/// InFront solver positions an object 2m in front of the tracked transform target
/// </summary>
public class InFront : Solver
{
...
public override void SolverUpdate()
{
if (SolverHandler != null && SolverHandler.TransformTarget != null)
{
var target = SolverHandler.TransformTarget;
GoalPosition = target.position + target.forward * 2.0f;
}
}
}
ソルバーの実装ガイド
ソルバーの共通のプロパティ
すべてのソルバー コンポーネントには、主要なソルバーの動作を制御する同一のプロパティのコア セットがあります。
[スムージング] が有効になっている場合、ソルバーでは、GameObject の変換を計算された値に時間の経過とともに段階的に更新します。 この変更の速度は、すべての変換コンポーネントの LerpTime プロパティによって決まります。 たとえば、MoveLerpTime 値を大きくすると、フレーム間の移動の増分が遅くなります。
MaintainScale が有効になっている場合、ソルバーでは GameObject の既定のローカル スケールが使用されます。
すべてのソルバー コンポーネントによって継承される共通のプロパティ
Orbital
Orbital
クラスは、太陽系の惑星のように動作するタグに沿ったコンポーネントです。 このソルバーでは、アタッチされた GameObject が追跡対象の変換の周りを回るようになります。 したがって、SolverHandler
の [追跡対象の種類] が Head
に設定されている場合、GameObject は固定されたオフセットが適用された状態でユーザーの頭の周りを回るようになります。
開発者は、この固定されたオフセットを変更して、メニューやその他のシーン コンポーネントをユーザーの周りの目の高さまたは腰の高さに維持することができます。 これを行うには、[ローカル オフセット] および [ワールド オフセット] プロパティを変更します。 [方向の種類] プロパティでは、オブジェクトで元の回転を維持するか、常にカメラの方を向くか、位置に影響する変換の方を向くかなど、オブジェクトに適用される回転を決定します。
Orbital の例
RadialView
RadialView
は、ユーザーの視錐台内に GameObject の特定の部分を保持する、もう 1 つのタグに沿ったコンポーネントです。
[最小ビュー深度] および [最大ビュー深度] プロパティでは、常に表示する必要がある GameObject の部分の大きさを決定します。
[最小距離] および [最大距離] プロパティでは、GameObject とユーザーの間に維持する距離を決定します。 たとえば、[最小距離] が 1m の GameObject に向かって歩くと、そのユーザーの 1m 以内に近づかないように GameObject が退きます。
一般に、RadialView
は、Head
に設定された [追跡対象の種類] と組み合わせて使用されます。これにより、コンポーネントはユーザーの視線入力に従います。 ただし、このコンポーネントは、すべての [追跡対象の種類] の "ビュー" で維持されるように機能できます。
RadialView の例
フォロー
Follow
クラスでは、ローカルの前方軸を基準として、追跡対象のターゲットの前に要素が配置されます。 追跡対象がユーザー定義の境界を超えて移動するまで、要素を緩やかに制限して従わないように (つまりタグに沿うように) することができます。
これは、[最大水平ビュー深度] および [最大垂直ビュー深度] を管理するための追加のコントロールと、オブジェクトの [方向] を変更するためのメカニズムを使用すると、RadialView ソルバーと同様に動作します。
Follow のプロパティ
Follow のシーンの例 (Assets/MRTK/Examples/Demos/Solvers/Scenes/FollowSolverExample.unity)
InBetween
InBetween
クラスでは、アタッチされた GameObject が 2 つの変換の間に維持されます。 これら 2 つの変換エンドポイントは、GameObject の独自の SolverHandler
の [追跡対象の種類] と InBetween
コンポーネントの [2 番目の 追跡対象の種類] プロパティによって定義されます。 通常は、両方の種類が CustomOverride
に設定され、その結果の SolverHandler.TransformOverride
および InBetween.SecondTransformOverride
値が 2 つの追跡対象エンドポイントに設定されます。
実行時には、[2 番目の追跡対象の種類] および [2 番目の変換のオーバーライド] プロパティに基づいて、InBetween
コンポーネントによってもう 1 つの SolverHandler
コンポーネントが作成されます。
PartwayOffset
では、2 つの変換の間のラインに沿って、中間に 0.5、最初の変換に 1.0、2 番目の変換に 0.0 を使用して、オブジェクトを配置する場所を定義します。
InBetween ソルバーを使用して 2 つの変換の間にオブジェクトを保持する例
SurfaceMagnetism
SurfaceMagnetism
は、一連のサーフェスの LayerMask に対してレイキャストを実行し、その接触ポイントに GameObject を配置することで機能します。
[サーフェスの法線のオフセット] では、サーフェス上のヒットしたポイントでの法線の方向に、サーフェスからメートル単位で設定された距離に GameObject が配置されます。
反対に、[サーフェス レイのオフセット] では、実行されたレイキャストと逆の方向に、サーフェスからメートル単位で設定された距離に GameObject が配置されます。 したがって、レイキャストがユーザーの視線入力である場合、GameObject はサーフェス上のヒットしたポイントからカメラまでのラインに沿って近づきます。
[方向モード] では、サーフェス上の法線を基準にして適用される回転の種類が決定されます。
- [None] - 回転は適用されません
- [TrackedTarget] - レイキャストが実行される追跡対象の変換の方にオブジェクトが向きます
- [SurfaceNormal] - サーフェス上のヒットしたポイントの法線に基づいてオブジェクトが調整されます
- [Blended] - オブジェクトは、サーフェス上のヒットしたポイントの法線と、追跡対象の変換の方向に基づいて調整されます。
関連付けられている GameObject を [None] 以外のモードで垂直に保つには、[方向を垂直に維持する] を有効にします。
Note
[方向のブレンド] プロパティを使用すると、[方向モード] が [Blended] に設定されている場合の回転の要因のバランスを制御できます。 値 0.0 の場合は [TrackedTarget] モードによって方向が完全に決定され、値 1.0 の場合は [SurfaceNormal] によって方向が完全に決定されます。
ヒットできるサーフェスを判別する
SurfaceMagnetism
コンポーネントを GameObject に追加する場合は、GameObject とその子のレイヤーを考慮することが重要です (コライダーがある場合)。 このコンポーネントでは、さまざまな種類のレイキャストを実行して、それ自体に「磁性」を持たせるサーフェスを判別することによって機能します。 ソルバーの GameObject で SurfaceMagnetism
の MagneticSurfaces
プロパティにリストされているいずれかのレイヤーにコライダーがある場合、レイキャストはそれ自体にヒットし、その結果、GameObject が独自のコライダー ポイントにアタッチされます。 この奇妙な動作を回避するには、メインの GameObject とすべての子を "レイキャストを無視" レイヤーに設定するか、MagneticSurfaces
の LayerMask 配列を適切に変更します。
逆に、SurfaceMagnetism
の GameObject は MagneticSurfaces
プロパティにリストされていないレイヤー上のサーフェスとヒットしません。 通常は、必要なすべてのサーフェスを専用のレイヤー (つまり、Surfaces) に配置し、MagneticSurfaces
プロパティをこのレイヤーのみに設定することをお勧めします。 [既定] または [すべて] を使用すると、UI コンポーネントまたはカーソルがソルバーに寄与する可能性があります。
最後に、MaxRaycastDistance
プロパティ設定よりも遠いサーフェスは、SurfaceMagnetism
のレイキャストによって無視されます。
DirectionalIndicator
DirectionalIndicator
クラスは、スペース内の目的のポイントの方向にそれ自体を向ける、タグに沿ったコンポーネントです。
SolverHandler
の [追跡対象の種類] が Head
に設定されている場合に最もよく使用されます。 このように、DirectionalIndicator
ソルバーを使用する UX コンポーネントでは、スペース内の目的のポイントを見るようにユーザーを導きます。
スペースの目的のポイントは、[方向のターゲット] プロパティを使用して決定されます。
ユーザーがその方向のターゲットを見ることができる場合、または SolverHandler
に何らかの参照のフレームが設定されている場合、このソルバーによってその下にあるすべての Renderer
コンポーネントが無効にされます。 見ることができない場合は、インジケーターですべてが有効になります。
インジケーターのサイズは、ユーザーが FOV で [方向のターゲット] のキャプチャに近づくほど縮小します。
[最小インジケーター スケール] - インジケーター オブジェクトの最小スケール
[最大インジケーター スケール] - インジケーター オブジェクトの最大スケール
[可視性スケール ファクター] - [方向のターゲット] ポイントを表示できるかどうかを決定する FOV を増減するための乗数
表示オフセット - このプロパティでは、参照フレームの視点で (カメラなど)、ビューポートの中心からインジケーター方向へのオブジェクトの距離を定義します。
方向インジケーターのプロパティ
方向インジケーターのシーンの例 (Assets/MRTK/Examples/Demos/Solvers/Scenes/DirectionalIndicatorSolverExample.unity)
HandConstraint と HandConstraintPalmUp を含むハンド メニュー
HandConstraint
の動作では、手に制約されるコンテンツ (ハンド UI、メニューなど) で追跡対象のオブジェクトを安全な領域に制約するソルバーが提供されます。 安全な領域は、手と交差しない領域と見なされます。 手のひらがユーザーの方を向いているときに、ソルバーの追跡対象のオブジェクトをアクティブ化する一般的な動作を示すために、HandConstraintPalmUp
と呼ばれる HandConstraint
の派生クラスも含まれています。
ハンド制約ソルバーを使用してハンド メニューを作成する例については、「ハンド メニュー」のページを参照してください。