求解器是根據預先定義的演算法,協助計算物件位置與方向的元件。 舉例:將一個物體放置在使用者的凝視射線投射與其交會的表面上。
Solver 系統會確定性地定義這些轉換計算的操作順序,因為沒有可靠的方法可以向 Unity 指定元件的更新順序。
求解器提供多種行為,將物件附加到其他物件或系統上。 另一個例子是根據攝影機懸浮在使用者前方的跟蹤物體。 解算器也可以附加在控制器和物件上,使物件標記在控制器上。 所有求解器都可以安全堆疊——例如,跟隨行為加上表面磁性加上動量。
如何使用
解算器系統由三類腳本組成:
-
Solver:所有求解器都源自的抽象基底類別。 它提供狀態追蹤、參數平滑與實作、自動求解系統整合,以及更新順序。 -
SolverHandler:將參考物件設定為 (例:主攝影機轉換、手部光線 ) 等,處理求解器元件的收集,並依正確順序執行更新。
第三類是求解器本身。 以下解算器提供基本行為的基礎組件:
-
Orbital:鎖定於指定位置及偏移量。 -
ConstantViewSize:縮放以維持相對於參考物件視圖的恆定大小。 -
RadialView:保持物件在參考物件投射的視錐內。 -
Follow:將物件保持在被參考物件的使用者定義範圍內。 -
InBetween:將一個物體置於兩個追蹤物體之間。 -
SurfaceMagnetism:將射線投射到世界中的表面,並將物件對齊到該表面。 -
DirectionalIndicator:作為方向指示器,決定物體的位置與方向。 從 SolverHandler 追蹤目標的參考點出發,此指示器會朝向所提供的方向目標。 -
Momentum:應用加速度/摩擦力來模擬被其他解算器/元件移動物體的動量與彈性。 -
HandConstraint: 約束物件跟隨手部,在不與 GameObject 與手部相交的區域。 對於手動限制的互動內容(如選單等)非常有用。此求解器設計用於XRNode。 -
HandConstraintPalmUp: 源自 HandConstraint,但包含測試手掌是否面向使用者的邏輯。 這個求解器只適用於XRNode控制器,且在其他控制器類型中會像它的基礎類別一樣運作。 -
Overlap:與追蹤物體重疊。
要使用 Solver 系統,請將上述其中一個元件加入 GameObject。 由於所有求解器都需要一個 SolverHandler,Unity 會自動建立一個。
注意事項
Solvers 系統的使用範例可在 SolverExamples.scene 檔案中找到。
如何更改追蹤參考
元件的SolverHandler追蹤目標類型屬性定義了所有求解器用來計算演算法的參考點。 例如,一個帶有簡單SurfaceMagnetism分量的值型態Head會產生從頭部投射的光線,並朝使用者視線方向投射,以解決被擊中的表面。 該 TrackedTargetType 物業的潛在價值如下:
- *頭部:參考點是主攝影機的變換
-
ControllerRay:參考點是
LinePointer控制器 (上的變換,也就是動作控制器或手控制器的指標原點指向線條射線方向)- 使用該
TrackedHandedness屬性選擇慣用性偏好 (,即左、右、兩者皆)
- 使用該
-
HandJoint:參考點是特定手關節的變換
- 使用該
TrackedHandedness屬性選擇慣用性偏好 (,即左、右、兩者皆) - 利用該
TrackedHandJoint性質來決定要使用的聯合變換
- 使用該
-
CustomOverride:來自被指派者的參考點
TransformOverride
注意事項
對於 ControllerRay 和 HandJoint 類型,求解器處理器會先嘗試提供左手控制器/手的轉換,若前者不可用或 TrackedHandedness 屬性另有說明,則嘗試提供右手。
重要事項
大多數求解器使用由 SolverHandler提供的追蹤轉換目標的前向向量。 使用手 關節 追蹤目標類型時,掌關節的前向量可能指向手指,而非掌心。 這取決於提供手關節資料的平台。 在輸入模擬和Windows Mixed Reality中,向上向量會從掌心向上 (換句話說,綠色向量向上,藍色向量向前) 。
為了克服這個問題,請將 的 SolverHandlerAdditional Rotation 屬性更新為 <90, 0, 0>。 這確保提供給求解器的前向向量指向手掌並向外遠離手部。
或者,使用 控制器光線 追蹤目標類型,讓用手指向時有類似的行為。
如何串接求解器
可以將多個 Solver 元件加入同一個遊戲物件,從而串連它們的演算法。 這些 SolverHandler 元件負責更新同一個 GameObject 上的所有解算器。 預設情況下, SolverHandler 呼叫 GetComponents<Solver>() 會設定在 Start,會依照在檢查器中出現的順序回傳求解器。
此外,將 Updated Linked Transform 屬性設為 true,會指示其 Solver 將計算出的位置、方向與縮放儲存為所有求解器可存取的中間變數,也就是 GoalPosition) (。 如果是錯誤,則 Solver 會直接更新 GameObject 的轉換。 透過將轉換屬性儲存到中間位置,其他求解器即可從中間變數開始進行計算。 這是因為 Unity 不允許 gameObject.transform 的更新在同一幀內堆疊。
注意事項
開發者可直接設定 SolverHandler.Solvers 屬性,修改解算器的執行順序。
如何建立新的解算器
所有求解器必須繼承自抽象基底類別 Solver。 求解器擴充的主要需求是覆寫該 SolverUpdate 方法。 在此方法中,開發者應將繼承的 GoalPosition、 GoalRotation及 GoalScale 屬性更新為所需的值。 此外,消費者 SolverHandler.TransformTarget 希望能以此作為參考框架,這也很有價值。
以下程式碼範例說明了一個新的求解器元件,該 InFront 元件將附加物件置於 前方 2 公尺 SolverHandler.TransformTarget處。 消費者將 設定 SolverHandler.TrackedTargetType 為 Head,則 SolverHandler.TransformTarget 將是相機轉換,因此這個求解器會在每幀將附加的 GameObject 放置在使用者視線前方 2 公尺處。
/// <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 ,求解器將利用遊戲物件的預設本地縮放。
軌道
這個 Orbital 類別是一個跟著的組件,行為類似太陽系中的行星。 這個求解器會確保附加的 GameObject 繞著追蹤的變換軌道運行。 因此,如果 的SolverHandler追蹤目標類型設為 Head,則遊戲物件會繞著使用者頭部旋轉,並套用固定偏移。
開發者可以修改這個固定偏移,讓選單或其他場景元件保持在使用者周圍的視線高度、腰部等高度。 這是透過更改 Local Offset 和 World Offset 屬性來達成的。 Orientation Type 屬性決定了物件是否應該維持原始旋轉,或始終面向相機或驅動其位置的變換。
RadialView
這是 RadialView 另一個跟行元件,將遊戲物件的特定部分保持在使用者視角的視角內。
最小 & 最高視角度數屬性決定了遊戲物件必須有多少部分始終在視野中。
最小距離 & 最大距離屬性決定了遊戲物件應該與使用者保持多遠。 例如,當最小 距離 為 1 公尺時,向遊戲物件走去會將遊戲物件推開,確保它不會離使用者超過 1 公尺。
通常,該 RadialView 與 Tracked Target Type 一同使用, Head 使元件跟隨使用者的視線。 然而,這個元件可以被設定在任何追蹤目標類型的「視圖」中。
跟進
該 Follow 類別將一個元素相對於被追蹤目標的本地前方軸線置於前方。 該元素可以被寬鬆限制 (也稱為「跟車」) 直到追蹤目標超出用戶定義範圍後才會跟隨。
其運作方式類似 RadialView 求解器,新增控制項可管理 最大水平 & 垂直視角 度數,並有機制可調整物件 方向。
中間
這個 InBetween 類別會將附加的 GameObject 保留在兩個轉換之間。 GameObject 自身SolverHandler的 Tracked Target Type 與InBetween元件的 Second Tracked Target Type 屬性定義了這兩個轉換端點。 通常,兩種類型都會設為, CustomOverride 結果 SolverHandler.TransformOverride 和 InBetween.SecondTransformOverride 值則分別設定為兩個追蹤端點。
元件InBetween會在執行時根據第二追蹤目標類型(Second Tracked Target Type)和第二次轉換覆寫(Second Transform Override)屬性建立另一個SolverHandler元件。
在兩個轉換之間的直線上,定義 PartwayOffset 了物件的放置位置,0.5 為中間,第一次轉換為 1.0,第二次轉換為 0.0。
SurfaceMagnetism
它的 SurfaceMagnetism 運作方式是對一組 LayerMask 的表面進行射線投射,並將 GameObject 放在該接觸點。
Surface Normal Offset 會將 GameObject 放置在距離表面一個設定距離(公尺)的位置,方向是法線在表面的命中點方向。
相反地, 表面光線偏移 會將遊戲物件放置在距離表面一個設定距離(公尺)但方向與射線投射方向相反的位置。 因此,如果射線投射是使用者的視線,遊戲物件會沿著從地面命中點到鏡頭的線條靠近。
定向模式決定了相對於表面法線應施加的旋轉類型。
- 無 - 不使用輪換
- TrackedTarget - 物件會面向被追蹤的轉換,驅動射線投射
- SurfaceNormal - 物件會在表面的命中點根據法線對齊
- 混合 - 物件會根據表面的法線和追蹤的變換面向來對齊。
若要強制相關的 GameObject 在非「 無」以外的任何模式中保持垂直,請啟用 「保持垂直方向」。
注意事項
當方向模式設為混合時,使用方向混合屬性來控制旋轉因子之間的平衡。 0.0 的方向完全由 TrackedTarget 模式驅動,1.0 的方向完全由 SurfaceNormal 驅動。
Overlap
這是一個 Overlap 簡單的求解器,能讓物件的轉換保持與轉換目標相同的位置和旋轉 SolverHandler's 。
判斷可擊中哪些表面
在將元件加入 SurfaceMagnetism GameObject 時,重要的是要考慮 GameObject 及其子節點的層級(如果有碰撞器)。 該元件透過執行各種射線投射來決定要「磁化」在哪個表面。 假設求解器 GameObject 在屬性中列出MagneticSurfacesSurfaceMagnetism的某一層上有一個碰撞器。 在這種情況下,射線投射很可能會撞到自己,導致 GameObject 附著到自己的碰撞點上。 這種奇怪的行為可以透過將主 GameObject 和所有子物件設為 忽略光線投射 圖層,或適當修改 MagneticSurfaces LayerMask 陣列來避免。
相反地, SurfaceMagnetism GameObject 不會與該屬性中未列出 MagneticSurfaces 的圖層表面發生碰撞。 我們建議你將所有想要的表面放在專用圖層 (也就是 Surfaces) ,並將屬性設 MagneticSurfaces 為僅該圖層。 使用 預設 值或 所有 選項可能會導致使用者介面元件或游標對求解器產生貢獻。
最後,超出屬性設定的 MaxRaycastDistance 表面會被射線投射忽略 SurfaceMagnetism 。
方向指示器
該 DirectionalIndicator 類別是一個跟班組件,會朝向空間中目標點的方向。 當追蹤SolverHandler目標類型設為 Head時,最常使用。 透過這種方式,帶有 DirectionalIndicator 求解器的使用者體驗元件會引導使用者觀看所需的空間點。 此點由 方向目標 特性決定。
如果使用者或設定在 SolverHandler中設定的參考系能看到方向目標,則該求解器會停用其下方的所有 Renderer 元件。 如果無法顯示,指示器上的所有功能都會被啟用。
指示器的大小會隨著使用者越接近捕捉 方向目標(FOV )而縮小。
最小指示尺度 - 指示物的最小尺度
最大指示尺度 - 指示器物件的最大尺度
可見度比例因子 - 乘數可增加或減少視野,決定 方向目標 點是否可見
視角偏移 -從參考系的視角 (即相機可能) 且指示器方向,此特性定義物體距離視窗中心的距離。
方向指示器範例 場景 (資產/MRTK/範例/示範/求解器/場景/方向指示器SolverExample.Unity)
手部選單搭配 HandConstraint 和 HandConstraintPalmUp
此 HandConstraint 行為提供一個求解器,將追蹤物件限制在手部受限內容 ((如手部介面、選單等)的安全區域 ) 安全區域被視為不與手部相交的區域。 還包含一個衍生類別的 call HandConstraintHandConstraintPalmUp ,以展示當手掌面向使用者時啟動解算器追蹤物件的常見行為。
請參閱 Hand Menu 文件 ,了解如何使用 Hand Constraint 求解器來建立手型選單的範例。