コールバック
はじめに
Quantumにおけるコリジョンとトリガーのコールバックは「システムシグナル」を通して処理されます。特定のエンティティでコールバックを実行するには、2つのステップが必要です。
- エンティティに対して特定の型のコールバックを有効にする
- 対応するシグナルを実装する
コールバックを有効にしたり対応するコードを記述したりする方法を学ぶ前に、それぞれのコールバック型とそれらが実行されるタイミングを理解することが重要です。
コールバック型
コリジョンとトリガーは、2つのエンティティまたはエンティティと静的オブジェクトのペアに対して、物理エンジンの衝突判定ステップから生成されます。エンティティにアタッチされた物理コンポーネントの組み合わせや、トリガーのプロパティ値(true/false)によって、以下の表に示すコールバック型が実行されます。
エンティティ vs エンティティ
コンポーネント構成によって、物理が有効なエンティティは次のように分類されます。
- Non-Trigger Collider:エンティティはトリガーではない
PhysicsCollider
を持ち、オプションでキネマティックPhysicsBody
を持ちます。 - Trigger Collider:エンティティはトリガーの
PhysicsCollider
を持ち、オプションでキネマティックPhysicsBody
を持ちます。 - Dynamic Body:エンティティはトリガーではない
PhysicsCollider
とダイナミック(非キネマティック)PhysicsBody
を持ちます。
2つのエンティティAとBのペアの衝突について、以下のコールバックが実行される可能性(各エンティティが属するグループに依存)があります。
エンティティ A x B | Non-Trigger Collider | Trigger Collider | Dynamic Body |
---|---|---|---|
Non-Trigger Collider | なし | OnTrigger | OnCollision |
Trigger Collider | OnTrigger | なし | OnTrigger |
Dynamic Body | OnCollision | OnTrigger | OnCollision |
エンティティ vs 静的コライダー
一方で静的コライダーは、IsTrigger
プロパティに応じて、トリガーが非トリガーのいずれかになります。
エンティティと静的コライダーのペアの衝突について、可能性がある組み合わせは以下の通りです。
コンポーネント(エンティティと静的コライダー) | Non-Trigger Static Collider | Trigger Static Collider |
---|---|---|
Non-Trigger Collider | なし | OnTrigger |
Trigger Collider | OnTrigger | なし |
Dynamic Body | OnCollision | OnTrigger |
エンティティに対するコールバックの有効化
各エンティティに対して、どのコールバック型を有効にするか(および、どのコライダーを組み合わせるか)を制御することができます。これを行うには、UnityのQuantum Entity Prototype
から設定するか、物理エンジンAPIのSetCallbacks
関数にエンティティと衝突コールバックフラグを渡します。
エンティティプロトタイプから設定
物理コールバックは、PhysicsCollider
(2D/3D)を持つ任意のEntity Prototype
で設定できます。

各エンティティでは、複数のコールバックを有効にできます。
注意: Quantum Entity Prototype
上でコールバックを有効にしても、その特定のエンティティに対してコールバックが設定されるのみです。コードで対応する「シグナル」を実装する必要があることに注意してください。コールバックシグナルについての詳細は、後述のセクションをご覧ください。
コリジョンを有効にする際は、レイヤーマトリックスのレイヤーが適切にマッチしていることを確認してください。デフォルトでは、コライダーはゲームオブジェクトに対応したレイヤーを持ちますが、フィールドLayer Source
から明示的に定義を変更することができます。
コードから設定
コールバックフラグはビットマスクで、ビット演算を使用して複数のコールバック型を指定できます。
次のスニペットは、別のダイナミックなエンティティに対するOnTrigger
コールバック一式(OnDynamicTrigger
・OnDynamicTriggerEnter
・OnDynamicTriggerExit
)を有効にします。
C#
CallbackFlags flags = CallbackFlags.OnDynamicTrigger;
flags |= CallbackFlags.OnDynamicTriggerEnter;
flags |= CallbackFlags.OnDynamicTriggerExit;
// 2Dの場合
f.Physics2D.SetCallbacks(entity, flags);
// 3Dの場合
f.Physics3D.SetCallbacks(entity, flags);
以下は基本的なコールバックフラグです(対応するシグナルは、物理エンジンで衝突が検知されなくなるまで毎ティック呼び出されます)。
- CallbackFlags.OnDynamicCollision
- CallbackFlags.OnDynamicTrigger
- CallbackFlags.OnStaticTrigger
そして以下は、対応するEnter/Exitコールバックです(上記のコールバックと独立して有効にできます)。
CallbackFlags.OnDynamicCollisionEnter
CallbackFlags.OnDynamicCollisionExit
CallbackFlags.OnDynamicTriggerEnter
CallbackFlags.OnDynamicTriggerExit
CallbackFlags.OnStaticCollisionEnter
CallbackFlags.OnStaticCollisionExit
CallbackFlags.OnStaticTriggerEnter
CallbackFlags.OnStaticTriggerExit
エンティティごとにコールバックを有効にする必要があるのは、シミュレーションをできる限り速くするための意図的な設計です。またEnter/Exitコールバックは(基本的なコールバックと比較して)わずかに多くのメモリとCPUが使用されるため、できるだけ軽量なシミュレーションを軽量にするには、可能な限り使用を避けてください。
コールバックシグナル
注意: コリジョンとトリガーのコールバックは、「エンティティ vs エンティティ」「エンティティ vs 静的コライダー」の両方で統一したシグナルAPIが使用できます。
以下は2D物理のシグナルです。
C#
namespace Quantum {
public interface ISignalOnCollision2D : ISignal {
void OnCollision2D(Frame frame, CollisionInfo2D info);
}
public interface ISignalOnCollisionEnter2D : ISignal {
void OnCollisionEnter2D(Frame frame, CollisionInfo2D info);
}
public interface ISignalOnCollisionExit2D : ISignal {
void OnCollisionExit2D(Frame frame, ExitInfo2D info);
}
public interface ISignalOnTrigger2D : ISignal {
void OnTrigger2D(Frame frame, TriggerInfo2D info);
}
public interface ISignalOnTriggerEnter2D : ISignal {
void OnTriggerEnter2D(Frame frame, TriggerInfo2D info);
}
public interface ISignalOnTriggerExit2D : ISignal {
void OnTriggerExit2D(Frame frame, ExitInfo2D info);
}
}
以下は3D物理のシグナルです。
C#
namespace Quantum {
public interface ISignalOnCollision3D : ISignal {
void OnCollision3D(Frame frame, CollisionInfo3D info);
}
public interface ISignalOnCollisionEnter3D : ISignal {
void OnCollisionEnter3D(Frame frame, CollisionInfo3D info);
}
public interface ISignalOnCollisionExit3D : ISignal {
void OnCollisionExit3D(Frame frame, ExitInfo3D info);
}
public interface ISignalOnTrigger3D : ISignal {
void OnTrigger3D(Frame frame, TriggerInfo3D info);
}
public interface ISignalOnTriggerEnter3D : ISignal {
void OnTriggerEnter3D(Frame frame, TriggerInfo3D info);
}
public interface ISignalOnTriggerExit3D : ISignal {
void OnTriggerExit3D(Frame frame, ExitInfo3D info);
}
}
コールバックを受け取るには、アクティブなシステムの中の1つ(無効なシステムではシグナルは実行されません)に対応するシグナルインターフェースを実装してください。
C#
public class PickUpSystem : SystemSignalsOnly, ISignalOnTriggerEnter3D
{
public void OnTriggerEnter3D(Frame frame, TriggerInfo3D info)
{
if (!frame.Has<PickUpSlot>(info.Entity)) return;
if (!frame.Has<PlayerID>(info.Other)) return;
var item = frame.Get<PickUpSlot>(info.Entity).Item;
var itemAsset = frame.FindAsset<ItemBase>(item.Id);
itemAsset.OnPickUp(frame, info.Other, itemAsset);
frame.Destroy(info.Entity);
}
}
上記のコードは、シグナル実装時に使用できる最適化の例を示しています。SystemSignalsOnly
を継承することで、(タスクシステムの不要なオーバーヘッドを招く)空のUpdate関数をスケジュールせずに、シグナルを処理できます。
CollisionInfo
OnCollisionEnter
とOnCollision
のシグナルは、CollisionInfo
構造体から衝突したエンティティの追加情報を取得できます。
接触点
接触点の情報は、ContactPoints
APIから取得できます。
Average
: すべての接触点の平均Count
: 接触点の数Length
: すべての接触点を含むバッファFirst
: 最初の三角形の最初の接触点を返す
単一/任意の接触点のみが必要で平均点である必要はない場合は、First
で計算を節約できます。
ContactPoints
はイテレーターでもあります。メッシュに衝突した際は、すべての三角形の衝突の接触点を反復処理することができます。
C#
while(info.ContactPoints.Next(out var cp)) {
Draw.Sphere(cp, radius);
}
球状の三角形メッシュの衝突の接触点は1つであるため、Average
とContactPoints[0]
は同じ接触点を返します。その他の種類の衝突は、複数の接触点を持つ可能性があります。
メッシュ同士の衝突
エンティティがメッシュに衝突する際、Count
とAverage
はそのメッシュのすべての三角形の衝突が考慮されます。
エンティティ衝突時に各三角形のコールバックを受け取るのではなく、衝突した三角形が1つの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);
}
}
その他の情報とFAQ
コリジョンのコールバックを使用する際の有益な情報になります。
- 2つのエンティティが同じコールバックを設定している場合、コールバックはエンティティごとに1回ずつ呼び出されます。2回の呼び出しの違いは、
Entity
とOther
の値が入れ替わることです。 - トリガーや静的コライダーとの衝突では、法線/衝突点/浸透データは計算されません。
- Unityの静的コライダーは、
Asset
フィールドにQuantum DBアセットをアタッチできます。期待するカスタムアセット型にキャストすることで、静的なコリジョンのコールバックに任意のデータを追加できます。