Queries
簡介
查詢可能會考慮動態實體和靜態碰撞體。射線、線段和形狀重疊的 API 非常相似,因為它們總是返回一個碰撞集合(其欄位中包含相同類型的數據)。
每個物理查詢都可以使用可選標誌進行自定義,這使得它們非常靈活且經過優化。例如,默認情況下,大多數查詢不返回碰撞點或法線,這需要用戶明確定義(將在本文檔後面的選項主題中解釋)。
查詢
線段檢測和射線檢測
C#
// For 2D
var hits = f.Physics2D.LinecastAll(FPVector2.Zero, FPVector2.One);
for (int i = 0; i < hits.Count; i++) {
var hit = hits[i];
}
// For 3D
var hits = f.Physics3D.LinecastAll(FPVector3.Zero, FPVector3.One);
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
生成的 HitCollection 對象包含以下屬性:
- HitCollection 中的每個項都包含一個 EntityRef 或靜態碰撞體信息。它們是互斥的 —— 一個有效,另一個則為 null;
- 應始終使用 Count 來迭代 HitCollection;並且,
- 碰撞結果未排序。您可以通過調用
Sort()
並傳入一個 FPVector2 對它們進行排序,這將根據碰撞點到提供給該函數的參考點的距離對碰撞結果進行排序。
射線檢測是線段檢測的語法糖。它們的工作方式相同,只需要起點, 方向 和 最大距離,而不是起點 和 終點。此外,可以向線段檢測和射線檢測傳遞可選參數:
- 圖層遮罩,用於指定要對其執行檢測的物理圖層;以及,
- 查詢選項,用於指定檢測中要考慮的碰撞體類型。
形狀查詢
Quantum支持兩種不同類型的形狀查詢:
- ShapeOverlap;以及,
- ShapeCasts。
這些可用於 Quantum 中支持的所有動態形狀。
注意: CompoundShapes
可用於執行形狀查詢。有關更多信息,請閱讀 形狀配置 頁面。
ShapeOverlaps
OverlapShape()
返回一個 HitCollection。所需參數為:
- 中心位置 (FPVector2 或 FPVector3);
- 旋轉 (FP 或 FPQuaternion for the 3D equivalent); 以及,
- 形狀 (Shape2D 或 Shape3D - 來自 PhysicsCollider, 或在調用時創建)。
C#
// For 2D
var hits = f.Physics2D.OverlapShape(FPVector2.Zero, FP._0, Shape2D.CreateCircle(FP._1));
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
// For 3D
var hits = f.Physics3D.OverlapShape(FPVector3.Zero, FPQuaternion.Identity, Shape3D.CreateSphere(1));
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
ShapeCasts
ShapeCastAll()
返回一個 HitCollection。所需參數為:
- 中心位置 ( FPVector2 或 FPVector3);
- 形狀的旋轉 ( FP 或3D對應的 FPQuaternion);
- 形狀指針 ( _Shape2D* _ 或 Shape3D* - 來自 PhysicsCollider, 或在調用時創建); 以及,
- 以向量表示的距離和方向 ( FPVector2 或 FPVector3 )。
C#
// For 2D
var shape = Shape2D.CreateCircle(FP._1);
var hits = f.Physics2D.ShapeCastAll(FPVector2.Zero, FP._0, &shape, FPVector2.One);
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
// For 3D
var shape = Shape3D.CreateSphere(1);
var hits = f.Physics3D.ShapeCastAll(FPVector3.Zero, FPQuaternion.Identity, &shape, FPVector3.One);
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
形狀投射使用基於 GJK 的自定義算法。如果初始形狀投射位置包含在碰撞體內,除非啟用 QueryOptions
參數中的DetectOverlapsAtCastOrigin
標誌(這會使其在起始位置執行額外檢查),否則形狀投射無法檢測到與此類碰撞體的碰撞。
GJKConfig 設置在位於SimulationConfig
資產的Physics > GJKConfig
部分中。這些設置允許在精度和性能之間進行平衡,因為兩者都有權衡。默認值經過平衡,以適應常規大小的形狀。
Simplex Min/Max Bit Shift
: 通過逐步移位其原始值,允許在 Voronoy 單體中的點具有更好的精度,避免退化情況,同時不影響物理空間中位置的有效範圍。如果所涉及的形狀的比例和 / 或它們之間的距離非常小,考慮增加這些值。Shape Cast Max Iterations
: 算法在搜索低於嚴格公差的解時執行的最大迭代次數。增加它可能會導致更準確的結果,但在最壞情況下會犧牲性能,反之亦然。Shape Cast Hard Tolerance
: 低於此閾值的迭代結果(形狀之間的最近距離)可作為結束條件。減小它可能會導致更準確的結果,但會增加迭代次數,反之亦然。Shape Cast Soft Tolerance
: 如果在允許的最大迭代次數內未能找到低於定義的嚴格公差的可接受結果,形狀投射解析仍會返回正值,如果到目前為止找到的最佳結果低於此軟閾值。在這些情況下,增加此閾值會增加誤報的可能性,而減小它會增加漏報的可能性。
排序碰撞結果
所有返回 HitCollection
的查詢都可以排序。
Sort()
: 在 2D 中接受 FPVector2,在 3D 中接受 FPVector3,並根據碰撞點到提供的點的各自距離對集合進行排序。SortCastDistance()
: 用於對ShapeCast
查詢的結果進行排序。它不需要參數,並根據投射距離對碰撞結果進行排序。
選項
所有查詢,包括它們的廣義階段版本,都可以使用QueryOptions
來自定義操作及其結果。
QueryOptions
創建一個遮罩,用於過濾要考慮的對象類型和要計算的信息。可以使用二進制|
運算符將它們組合起來。
碰撞法線
為了提供性能最佳的查詢,所有默認查詢只檢查兩個形狀是否重疊。例如,默認情況下,大多數查詢不檢索碰撞點或法線。
為了接收額外的信息,需要更多的計算,這反過來會產生額外的開銷;因此,必須通過傳遞ComputeDetailedInfo
作為 QueryOptions 參數來明確指定它。這將啟用對碰撞的以下內容的計算:
- 點
- 法線
- 穿透深度
對於射線 - 三角形檢查,法線始終是三角形的法線。由於這緩存在三角形數據中,因此在這種情況下沒有額外的計算。
過濾碰撞結果
以下QueryOptions
允許您定義查詢使用的遮罩。如果對象與指定為參數的QueryOptions
不匹配,它將被跳過;只有與QueryOptions
匹配的對象才會被評估並返回到結果中。
HitStatics : 只會碰撞靜態碰撞體
HitKinematics : 會碰撞滿足以下任一條件的實體:
- 具有 PhysicsCollider 但 沒有 PhysicsBody 的實體
- 具有 PhysicsCollider 和 禁用的 PhysicsBody 的實體
- 具有 PhysicsCollider 和 運動學的 PhysicsBody 的實體
HitDynamics : 只會碰撞具有 已啟用 且 非運動學 PhysicsBody 的實體
HitTriggers : 必須與其他標誌 組合使用 才能碰撞觸發器碰撞體。
HitAll : 會碰撞所有靜態碰撞體和具有 PhysicsCollider 的實體,包括觸發器。
HitSolids : 會碰撞所有靜態碰撞體和具有 PhysicsCollider 的實體,但不包括觸發器碰撞體。
默認情況下,查詢將使用HitAll
選項。選擇任何其他選項都會節省計算量。
廣譜階段查詢
Quantum 提供了一個選項,可以注入物理查詢(射線投射和重疊)以在物理系統期間解析。為此,您需要:
- 創建一個系統;
- 將其添加到使用的系統配置資產中,位於
Core.PhysicsSystem
之前; - 在任何運行在
Core.PhysicsSystem
之後的系統中檢索信息。
此設置受益於物理步驟的并行解析,這使得它比物理之後的常規查詢明顯更快。

注意: 有時廣譜階段查詢也稱為注入查詢或計劃查詢,因為它們在求解器運行之前被計劃 / 注入到物理引擎中。
注入查詢
可以從任何在物理之前運行的主線程系統注入查詢。注入查詢將返回一個PhysicsQueryRef
,可以存儲它並用於在物理系統運行後檢索結果。廣義階段查詢結果旨在在注入查詢的同一幀內檢索,因此PhysicsQueryRef
可以存儲在任何地方 —— 包括可回滾的幀數據之外。
C#
namespace Quantum
{
public unsafe struct ProjectileFilter
{
public EntityRef EntityRef;
public Transform3D* Transform;
public Projectile* Component;
}
public unsafe class ProjectileHitQueryInjectionSystem : SystemMainThread
{
public override void Update(Frame frame)
{
var projectileFilter = frame.Unsafe.FilterStruct<ProjectileFilter>();
var projectile = default(ProjectileFilter);
while (projectileFilter.Next(&projectile))
{
projectile.Component->PathQueryRef = frame.Physics3D.AddRaycastQuery(
projectile.Transform->Position,
projectile.Transform->Forward,
projectile.Component->Speed * frame.DeltaTime);
var spec = frame.FindAsset<WeaponSpec>(projectile.Component->WeaponSpec.Id);
projectile.Component->DamageZoneQueryRef = frame.Physics3D.AddOverlapShapeQuery(
projectile.Transform->Position,
projectile.Transform->Rotation,
spec.AttackShape.CreateShape(frame),
spec.AttackLayers);
}
}
}
}
重要: 由AddXXXQuery
返回的PhysicsQueryRef
對於稍後檢索查詢結果絕對必要。因此,建議將其保存在附加到需要稍後處理碰撞的實體的組件中。
檢索查詢結果
可以從任何在核心物理系統之後運行的系統中檢索查詢結果。要檢索結果(HitCollection*),將先前保存的索引傳遞給Frame.Physics.GetQueryHits()
或.TryGetQueryHits()
。
嘗試使用無效的PhysicsQueryRef
(例如,從在不同幀中注入的查詢)檢索結果將在GetQueryHits
中拋出異常,或在TryGetQueryHits
中返回 false。
C#
using Photon.Deterministic;
namespace Quantum
{
public unsafe class ProjectileHitRetrievalSystem : SystemMainThread
{
public override void Update(Frame frame)
{
var projectileFilter = frame.Unsafe.FilterStruct<ProjectileFilter>();
var projectile = default(ProjectileFilter);
while (projectileFilter.Next(&projectile))
{
if (frame.Physics3D.TryGetQueryHits(projectile.Component->PathQueryRef, out var hitsOnTrajectory) == false || hitsOnTrajectory.Count <= 0)
{
projectile.Transform->Position =
projectile.Transform->Rotation *
projectile.Transform->Forward *
projectile.Component->Speed * frame.DeltaTime;
continue;
}
if (frame.Physics3D.TryGetQueryHits(projectile.Component->DamageZoneQueryRef, out var damageZoneHits))
{
for (int i = 0; i < damageZoneHits.Count; i++)
{
// Apply damage logic
}
}
}
}
}
}
此外,可以通過public bool GetAllQueriesHits(out HitCollection* queriesHits, out int queriesCount)
調用獲取所有廣義階段結果,該調用也可通過 Frame.Physics
獲得。
注意事項
使用廣譜階段查詢時需要記住的幾個要點:
- 對於大量查詢(例如子彈),性能大約提高 20 倍。
- 它們基於物理系統啟動前的幀狀態。
- 廣譜階段查詢不會在幀之間保留;也就是說,它們需要在物理之前的幀開始時注入。在物理運行後注入的廣義階段查詢永遠不會返回結果。這是因為 Quantum 的物理是無狀態的。
模擬CCD
Quantum 的物理引擎是無狀態的。在這樣的系統中,連續碰撞檢測會過於昂貴。在無狀態物理引擎中模擬 CCD 行為的解決方案通常涉及射線投射或形狀重疊,這些投射或重疊延伸到一幀的預期移動。
這個話題通常與快速移動的實體(如子彈)結合出現。根據快速移動物體的大小,我們建議使用以下方法之一:
- 沿移動方向的短射線,長度為
velocity * deltaTime
; 或, - 單個重疊;請注意,形狀重疊也可以使用復合形狀完成。
這些解決方案中的任何一個都會複製 100% 準確的 CCD,並總體上帶來更好的性能。為了進一步提高性能,可以將其與廣義階段查詢相結合。
Back to top