Callbacks
概述
Quantum中的碰撞及觸發回調透過 系統信號 被處理。讓回調針對任何特定實體來被執行,需要兩個步驟:
- 啟用針對實體的回調的特定類型。
- 執行相應的信號。
在學習如何啟用回調及如何撰寫程式碼之前,重要的是先了解不同的回調類型及哪些事情讓它們被執行。
回調類型
碰撞及觸發由兩個實體,或由物理引擎中的碰撞偵測步驟生成的一個實體靜態對來啟動。取決於附加於實體的物理元件的組合,及觸發屬性的值(真/偽),下表描述了將被執行的回調的類型。
實體 vs 實體
根據它們的元件組合,物理啟用的實體可分類如下:
- 非觸發碰撞器:有一個非觸發物理碰撞器及,可選地,一個運動學的物理主體。
- 觸發碰撞器:有一個觸發物理碰撞器及,可選地,一個運動學的物理主體。
- 動態主體:有一個非觸發物理碰撞器及一個動態(非運動學的)物理主體的實體。
當一個碰撞對由兩個實體A及B組成,這些可能被執行的回調(取決於各個實體屬於的群):
實體 A x B | 非觸發碰撞器 | 觸發碰撞器 | 動態主體 |
---|---|---|---|
非觸發碰撞器 | 無 | 觸發時 | 碰撞時 |
觸發碰撞器 | 觸發時 | 無 | 觸發時 |
動態主體 | 碰撞時 | 觸發時 | 碰撞時 |
實體 vs 靜態碰撞器
另一方面,靜態碰撞器可以是觸發或非觸發,這是根據它們的IsTrigger
屬性。
當碰撞對由一個實體及一個靜態碰撞器組成,這些是可能的結合:
元件(實體及靜態) | 非觸發靜態碰撞器 | 觸發靜態碰撞器 |
---|---|---|
非觸發碰撞器 | 無 | 觸發時 |
觸發碰撞器 | 觸發時 | 無 |
動態主體 | 碰撞時 | 觸發時 |
在實體上啟用回調
針對各個個別的實體,可以控制啟用哪個回調類型(以及針對哪個種類的其他碰撞器)。透過Unity中的 實體原型 或物理引擎API中的 設定回調 功能的程式碼來完成,其使用實體及一個碰撞回調旗標。
透過實體原型
在任何帶有一個 物理碰撞器 (2D/3D)的 實體原型 上可以設定物理回調。
各個實體可以有多個回調。
請注意: 在一個 實體原型 上啟用回調,只針對該特定實體 設定回調。您在程式碼中仍然 必須執行 相應的 信號。請見以下 回調信號 的章節以取得更多資訊。
透過程式碼
回調旗標是一個位元遮罩,並且可以使用位元運算來指定多個回調類型,如同以下的例子。
以下程式碼片段啟用了針對另一個動態實體(動態觸發時、動態觸發時動態觸發進入時,及動態觸發結束時)的觸發時回調的完整集合。
C#
CallbackFlags flags = CallbackFlags.OnDynamicTrigger;
flags |= CallbackFlags.OnDynamicTriggerEnter;
flags |= CallbackFlags.OnDynamicTriggerExit;
// for 2D
f.Physics2D.SetCallbacks(entity, flags);
// for 3D
f.Physics3D.SetCallbacks(entity, flags);
這些是基本的回調旗標(相應的信號在每個刷新被調用,直到物理引擎不再偵測到碰撞):
- CallbackFlags.OnDynamicCollision
- CallbackFlags.OnDynamicTrigger
- CallbackFlags.OnStaticTrigger
而這些是相應的進入/結束回調(其可從上述旗標來獨立地被啟用):
CallbackFlags.OnDynamicCollisionEnter
CallbackFlags.OnDynamicCollisionExit
CallbackFlags.OnDynamicTriggerEnter
CallbackFlags.OnDynamicTriggerExit
CallbackFlags.OnStaticCollisionEnter
CallbackFlags.OnStaticCollisionExit
CallbackFlags.OnStaticTriggerEnter
CallbackFlags.OnStaticTriggerExit
必須在各個實體的基礎上啟用回調,這是刻意的設計,以盡量加快預設模擬的速度。同時請注意,進入/結束回調使用稍微多的記憶體和更多的處理器使用量(相較於基本的回調),所以為了盡可能簡潔的模擬,您需要盡量避免這些。
回調信號
請注意: 針對 實體 vs 實體 及 實體 vs 靜態 對的碰撞及觸發回調,被分組到一個統一的信號API。
這些是2D物理信號:
C#
namespace Quantum {
public interface ISignalOnCollision2D : ISignal {
void OnCollision2D(Frame f, CollisionInfo2D info);
}
public interface ISignalOnCollisionEnter2D : ISignal {
void OnCollisionEnter2D(Frame f, CollisionInfo2D info);
}
public interface ISignalOnCollisionExit2D : ISignal {
void OnCollisionExit2D(Frame f, ExitInfo2D info);
}
public interface ISignalOnTrigger2D : ISignal {
void OnTrigger2D(Frame f, TriggerInfo2D info);
}
public interface ISignalOnTriggerEnter2D : ISignal {
void OnTriggerEnter2D(Frame f, TriggerInfo2D info);
}
public interface ISignalOnTriggerExit2D : ISignal {
void OnTriggerExit2D(Frame f, ExitInfo2D info);
}
}
以及3D物理信號:
C#
namespace Quantum {
public interface ISignalOnCollision3D : ISignal {
void OnCollision3D(Frame f, CollisionInfo3D info);
}
public interface ISignalOnCollisionEnter3D : ISignal {
void OnCollisionEnter3D(Frame f, CollisionInfo3D info);
}
public interface ISignalOnCollisionExit3D : ISignal {
void OnCollisionExit3D(Frame f, ExitInfo3D info);
}
public interface ISignalOnTrigger3D : ISignal {
void OnTrigger3D(Frame f, TriggerInfo3D info);
}
public interface ISignalOnTriggerEnter3D : ISignal {
void OnTriggerEnter3D(Frame f, TriggerInfo3D info);
}
public interface ISignalOnTriggerExit3D : ISignal {
void OnTriggerExit3D(Frame f, ExitInfo3D info);
}
}
為了收到回調,您需要在至少一個啟用的系統中(停用的系統不獲得任何在它們中執行的信號)執行相應的信號介面:
C#
public class PickUpSystem : SystemSignalsOnly, ISignalOnTriggerEnter3D
{
public void OnTriggerEnter3D(Frame f, TriggerInfo3D info)
{
if (!f.Has<PickUpSlot>(info.Entity)) return;
if (!f.Has<PlayerID>(info.Other)) return;
var item = f.Get<PickUpSlot>(info.Entity).Item;
var itemAsset = f.FindAsset<ItemBase>(item.Id);
itemAsset.OnPickUp(f, info.Other, itemAsset);
f.Destroy(info.Entity);
}
}
上述程式碼展示了執行信號時可以使用的另一種最佳化。繼承 僅系統信號 可以讓系統在處理信號的同時不需要排程一個空的更新函數(其將不必要地導致工作系統額外負荷)。
碰撞資訊
針對OnCollisionEnter
及OnCollision
的信號透過CollisionInfo
架構提供關於碰撞實體的額外的資訊。
聯絡點
聯絡點上的資訊可以透過ContactPoints
API來存取。
Average
:所有聯絡點的平均Count
:聯絡點的數量Length
:所有聯絡點的一個緩衝First
: 傳回第一個三角形的第一個聯絡點
如果只需要一個/任何聯絡點,並且它不需要是一個已平均的聯絡點,那麼First
可以幫助節省運算。
ContactPoints
也是一個迭代。當碰撞一個網格,它可用於迭代所有三角形碰撞聯絡點。
C#
while(info.ContactPoints.Next(out var cp)) {
Draw.Sphere(cp, radius);
}
球體-三角形 碰撞有一個單一聯絡點;因此Average
及ContactPoints[0]
將傳回同樣的聯絡點。其他碰撞類型可以有更多聯絡點。
網格碰撞
當一個實體碰撞到一個網格,Count
及Average
將屬於該特定網格的所有三角形碰撞都考慮在內。
這些碰撞三角形被分組到一個單一CollisionInfo
架構,而不是接收針對實體碰撞的各個三角形的回調。
在網格碰撞的情形,info.ContactNormal
及info.Penetration
將傳回該網格的三角形碰撞的平均值;同樣的資料可透過info.MeshTriangleCollisions.AverageNormal
及info.MeshTriangleCollisions.AveragePenetration
取得。
除了平均正常一個滲透,您可以迭代各個三角形碰撞,及存取特定資訊比如三角形資料本身。MeshTriangleCollisions
也是一個迭代器;它可用於迭代各個三角形的碰撞資料,及擷取網格特定的碰撞資料。
C#
if (info.IsMeshCollision) {
while(info.MeshTriangleCollisions.Next(out var triCollision)) {
Draw.Ray(triCollision.Triangle->Center, triCollision.ContactNormal * triCollision.Penetration);
}
}
雜項及問與答
使用碰撞回調時的有用資訊:
- 如果兩個實體有相同的回調集合,回調將被調用兩次——每個其關聯的實體一次。兩個調用的唯一不同是針對 實體 及 其他 的值,其將被交換。
- 有著觸發及靜態碰撞器的碰撞,不運算正常/點/滲透資料。
- Unity上的靜態碰撞器可以有一個Quantum DB資產附加到它們的「資產」欄位。投射它到您期待的自訂資產類型,是一個好的方式以新增任意資料到您的靜態碰撞回調。