ソルバー — MRTK3
ソルバーは、事前定義されたアルゴリズムに従って、オブジェクトの位置と方向の計算を容易にするコンポーネントです。 例: ユーザーの視線入力レイキャストが交差するサーフェス上にオブジェクトを配置します。
ソルバー システムでは、これらの変換計算の操作の順序を確定的に定義します。その理由は、コンポーネントの更新順序を Unity に指定する信頼できる方法がないためです。
ソルバーでは、オブジェクトを他のオブジェクトまたはシステムにアタッチするためのさまざまな動作が提供されます。 もう 1 つの例は、カメラに基づいてユーザーの前に浮遊する追従オブジェクトです。 ソルバーをコントローラーとオブジェクトにアタッチして、オブジェクトをタグに沿ったコントローラーにすることもできます。 すべてのソルバーを安全にスタックすることができます。たとえば、追従動作 + 表面磁性 + 勢いです。
使用方法
ソルバー システムは、次の 3 つのカテゴリのスクリプトで構成されます。
Solver
: すべてのソルバーの派生元である抽象基本クラス。 状態の追跡、パラメーターと実装のスムージング、自動的なソルバー システムの統合、更新順序が提供されます。SolverHandler
: 追跡する参照オブジェクト (例: メイン カメラの変換、ハンド レイなど) を設定して、ソルバー コンポーネントの収集を処理し、適切な順序でそれらの更新を実行します。
3 番目のカテゴリはソルバー自体です。 次のソルバーでは、基本動作のための構成要素が提供されます。
Orbital
: 参照されるオブジェクトから指定された位置およびオフセットにロックします。ConstantViewSize
: 参照されるオブジェクトのビューに対して一定のサイズが維持されるようにスケーリングします。RadialView
: 参照されるオブジェクトによってキャストされたビュー コーン内のオブジェクトを保持します。Follow
: 参照されるオブジェクトの一連のユーザー定義の境界内にオブジェクトを保持します。InBetween
: 2 つの追跡対象オブジェクトの間にオブジェクトを保持します。SurfaceMagnetism
: ワールド内のサーフェスにレイをキャストし、そのサーフェスにオブジェクトを合わせます。DirectionalIndicator
: オブジェクトの位置と方向を方向インジケーターとして決定します。 SolverHandler の追跡対象の参照ポイントから、このインジケーターは指定された DirectionalTarget の方法を向きます。Momentum
: 他のソルバー/コンポーネントによって移動されるオブジェクトの勢いと弾力性をシミュレートするために、加速/ベロシティ/摩擦を適用します。HandConstraint
: GameObject と手が交差しない領域で、オブジェクトが手に従うように制約します。 メニューなどの手に制約される対話型コンテンツで役に立ちます。このソルバーはXRNode
で使用することを目的としています。HandConstraintPalmUp
: HandConstraint から派生しますが、アクティブにされる前に、手のひらがユーザーの方を向いているかどうかをテストするロジックが含まれています。 このソルバーはXRNode
コントローラーでのみ機能し、他のコントローラーの種類では、その基本クラスと同様に動作します。Overlap
: 追跡対象オブジェクトと重なります。
ソルバー システムを使用するには、上記のいずれかのコンポーネントを 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
プロパティに他のものが指定されている場合を除き、右が提供されます。
重要
ほとんどのソルバーでは、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
の 2 m 前に配置する InFront
という新しいソルバー コンポーネントの例を示しています。 コンシューマーによって SolverHandler.TrackedTargetType
が Head
として設定されると、SolverHandler.TransformTarget
はカメラの変換になります。したがって、このソルバーによって、接続されている GameObject が、ユーザーの視線入力のすべてのフレームの 2 m 前に配置されます。
/// <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 は固定されたオフセットが適用された状態でユーザーの頭の周りを回るようになります。
開発者は、この固定されたオフセットを変更して、メニューやその他のシーン コンポーネントをユーザーの周りの目の高さや腰の高さなどに維持することができます。 これを行うには、[ローカル オフセット] および [ワールド オフセット] プロパティを変更します。 [方向の種類] プロパティでは、オブジェクトで元の回転を維持するか、常にカメラの方を向くか、位置に影響する変換の方を向くか、オブジェクトに適用される回転を決定します。
RadialView
RadialView
は、ユーザーの視錐台内に GameObject の特定の部分を保持する、もう 1 つのタグに沿ったコンポーネントです。
Min & Max View Degrees プロパティは、GameObject の一部が常に表示されている必要がある部分の量を決定します。
Min & Max Distance プロパティは、GameObject をユーザーから保持する距離を決定します。 たとえば、[最小距離] が 1 m の GameObject に向かって歩くと、そのユーザーの 1 m 以内に近づかないように GameObject が退きます。
一般に、RadialView
は、Head
に設定された [追跡対象の種類] と一緒に使用されます。これにより、コンポーネントはユーザーの視線入力に従います。 ただし、このコンポーネントは、すべての [追跡対象の種類] の "ビュー" で維持されるように機能できます。
フォロー
Follow
クラスでは、ローカルの前方軸を基準として、追跡対象のターゲットの前に要素が配置されます。 この要素は、追跡対象がユーザー定義の境界を越えて移動するまで従わないように、緩やかに制限できます ("追従" とも呼ばれます)。
RadialView ソルバーと同様に機能し、 Max 水平ビュー度と垂直ビュー度 オブジェクトの Orientationを変更するメカニズムを管理する追加のコントロールを使用します。
InBetween
InBetween
クラスでは、アタッチされた GameObject が 2 つの変換の間に維持されます。 GameObject 独自の SolverHandler
Tracked Target Type および InBetween
コンポーネントの Second Tracked Target Type プロパティは、これら 2 つの変換エンドポイントを定義します。 通常は、両方の種類が CustomOverride
に設定され、その結果の SolverHandler.TransformOverride
および InBetween.SecondTransformOverride
値が 2 つの追跡対象エンドポイントに設定されます。
[2 番目の追跡対象の種類] および [2 番目の変換のオーバーライド] プロパティに基づいて、実行時に、InBetween
コンポーネントによってもう 1 つの SolverHandler
コンポーネントが作成されます。
PartwayOffset
では、2 つの変換の間のラインに沿って、中間に 0.5、最初の変換に 1.0、2 番目の変換に 0.0 を使用して、オブジェクトを配置する場所を定義します。
SurfaceMagnetism
SurfaceMagnetism
は、一連のサーフェスの LayerMask に対してレイキャストを実行し、その接触ポイントに GameObject を配置することで機能します。
[サーフェスの法線のオフセット] では、サーフェス上のヒットしたポイントでの法線の方向に、サーフェスからメートル単位で設定された距離に GameObject が配置されます。
反対に、[サーフェス レイのオフセット] では、実行されたレイキャストと逆の方向に、サーフェスからメートル単位で設定された距離に GameObject が配置されます。 したがって、レイキャストがユーザーの視線入力である場合、GameObject はサーフェス上のヒットしたポイントからカメラまでのラインに沿って近づきます。
[方向モード] では、サーフェス上の法線を基準にして適用される回転の種類が決定されます。
- [None] - 回転は適用されません
- [TrackedTarget] - レイキャストが実行される追跡対象の変換の方にオブジェクトが向きます
- [SurfaceNormal] - サーフェス上のヒットしたポイントの法線に基づいてオブジェクトが調整されます
- [Blended] - オブジェクトは、サーフェス上のヒットしたポイントの法線と、追跡対象の変換の方向に基づいて調整されます。
関連付けられている GameObject を [None] 以外のモードで垂直に保つには、[方向を垂直に維持する] を有効にします。
Note
[方向のブレンド] プロパティを使用すると、[方向モード] が [Blended] に設定されている場合の回転の要因のバランスを制御できます。 値 0.0 の場合は [TrackedTarget] モードによって方向が完全に決定され、値 1.0 の場合は [SurfaceNormal] によって方向が完全に決定されます。
Overlap
Overlap
は、オブジェクトの変換をSolverHandler's
変換ターゲットと同じ位置と回転に維持する単純なソルバーです。
ヒットできるサーフェスを判別する
SurfaceMagnetism
コンポーネントを GameObject に追加する場合は、GameObject とその子のレイヤーを考慮することが重要です (コライダーがある場合)。 このコンポーネントは、さまざまなレイキャストを実行して、それ自体に「磁性」を持たせるサーフェスを判別することによって機能します。 ソルバーの GameObject で SurfaceMagnetism
の MagneticSurfaces
プロパティにリストされているいずれかのレイヤーにコライダーがあるとします。 その場合、レイキャストがそれ自体にヒットし、その結果、GameObject が独自のコライダー ポイントにアタッチされる可能性があります。 この奇妙な動作を回避するには、メインの GameObject とすべての子を "レイキャストを無視" レイヤーに設定するか、MagneticSurfaces
の LayerMask 配列を適切に変更します。
逆に、SurfaceMagnetism
の GameObject は MagneticSurfaces
プロパティにリストされていないレイヤー上のサーフェスとヒットしません。 必要なすべてのサーフェスを専用のレイヤー (つまり、Surfaces) に配置し、MagneticSurfaces
プロパティをこのレイヤーのみに設定することをお勧めします。 [既定] または [すべて] を使用すると、UI コンポーネントまたはカーソルがソルバーに寄与する可能性があります。
最後に、MaxRaycastDistance
プロパティ設定よりも遠いサーフェスは、SurfaceMagnetism
のレイキャストによって無視されます。
DirectionalIndicator
DirectionalIndicator
クラスは、スペース内の目的のポイントの方向にそれ自体を向ける、タグに沿ったコンポーネントです。 SolverHandler
の [Tracked Target Type] (追跡対象の種類) が Head
に設定されている場合に最もよく使用されます。 このように、DirectionalIndicator
ソルバーを使用する UX コンポーネントでは、スペース内の目的のポイントを見るようにユーザーを導きます。 このポイントは、[Directional Target] (方向のターゲット) プロパティによって決定されます。
ユーザーがその方向のターゲットを見ることができる場合、または SolverHandler
に参照のいずれかのフレームが設定されている場合、このソルバーによってその下にあるすべての Renderer
コンポーネントが無効にされます。 見ることができない場合は、インジケーターですべてが有効になります。
インジケーターのサイズは、ユーザーが FOV で [方向のターゲット] のキャプチャに近づくほど縮小します。
[最小インジケーター スケール] - インジケーター オブジェクトの最小スケール
[最大インジケーター スケール] - インジケーター オブジェクトの最大スケール
[Visibility Scale Factor] (可視性の倍率) - [Directional Target] (方向のターゲット) ポイントを表示できるかどうかを決定する FOV を増減するための乗数
表示オフセット - このプロパティでは、参照フレームの視点で (カメラなど)、ビューポートの中心からインジケーター方向へのオブジェクトの距離を定義します。
方向インジケーターのシーンの例 (Assets/MRTK/Examples/Demos/Solvers/Scenes/DirectionalIndicatorSolverExample.unity)
HandConstraint と HandConstraintPalmUp を含むハンド メニュー
この HandConstraint
の動作では、手で制約されるコンテンツ (ハンド UI、メニューなど) で追跡対象のオブジェクトを安全な領域に制約するソルバーが提供されます。安全な領域は、手と交差しない領域と見なされます。 手のひらがユーザーの方を向いているときに、ソルバーの追跡対象のオブジェクトをアクティブ化する一般的な動作を示すために、HandConstraintPalmUp
と呼ばれる HandConstraint
の派生クラスも含まれています。
ハンド制約ソルバーを使用してハンド メニューを作成する例については、「ハンド メニュー」のドキュメントを参照してください。