This document is about: QUANTUM 3
SWITCH TO

コールバック

はじめに

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で設定できます。

Setting Physics Callbacks via the Entity Prototype's Physics Properties in the Unity Editor
Unityエディター上で、エンティティプロトタイプの物理プロパティから、物理コールバックを設定

各エンティティでは、複数のコールバックを有効にできます。

注意: Quantum Entity Prototype上でコールバックを有効にしても、その特定のエンティティに対してコールバックが設定されるのみです。コードで対応する「シグナル」を実装する必要があることに注意してください。コールバックシグナルについての詳細は、後述のセクションをご覧ください。

コリジョンを有効にする際は、レイヤーマトリックスのレイヤーが適切にマッチしていることを確認してください。デフォルトでは、コライダーはゲームオブジェクトに対応したレイヤーを持ちますが、フィールドLayer Sourceから明示的に定義を変更することができます。

コードから設定

コールバックフラグはビットマスクで、ビット演算を使用して複数のコールバック型を指定できます。

次のスニペットは、別のダイナミックなエンティティに対するOnTriggerコールバック一式(OnDynamicTriggerOnDynamicTriggerEnterOnDynamicTriggerExit)を有効にします。

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

OnCollisionEnterOnCollisionのシグナルは、CollisionInfo構造体から衝突したエンティティの追加情報を取得できます。

接触点

接触点の情報は、ContactPointsAPIから取得できます。

  • Average : すべての接触点の平均
  • Count : 接触点の数
  • Length : すべての接触点を含むバッファ
  • First : 最初の三角形の最初の接触点を返す

単一/任意の接触点のみが必要で平均点である必要はない場合は、Firstで計算を節約できます。

ContactPointsはイテレーターでもあります。メッシュに衝突した際は、すべての三角形の衝突の接触点を反復処理することができます。

C#

while(info.ContactPoints.Next(out var cp)) {
  Draw.Sphere(cp, radius);
}

球状の三角形メッシュの衝突の接触点は1つであるため、AverageContactPoints[0]は同じ接触点を返します。その他の種類の衝突は、複数の接触点を持つ可能性があります。

メッシュ同士の衝突

エンティティがメッシュに衝突する際、CountAverageはそのメッシュのすべての三角形の衝突が考慮されます。
エンティティ衝突時に各三角形のコールバックを受け取るのではなく、衝突した三角形が1つのCollisionInfo構造体にグループ化されます。

メッシュ同士が衝突する場合、info.ContactNormalinfo.Penetrationは衝突したメッシュの三角形の平均値を返します。info.MeshTriangleCollisions.AverageNormalinfo.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回の呼び出しの違いは、EntityOtherの値が入れ替わることです。
  • トリガーや静的コライダーとの衝突では、法線/衝突点/浸透データは計算されません。
  • Unityの静的コライダーは、AssetフィールドにQuantum DBアセットをアタッチできます。期待するカスタムアセット型にキャストすることで、静的なコリジョンのコールバックに任意のデータを追加できます。
Back to top