This document is about: QUANTUM 3
SWITCH TO

コールバック

はじめに

Quantum における衝突とトリガーコールバックは システムシグナル を介して処理されます。特定のエンティティに対してコールバックを実行するためには、2つのステップが必要です:

  • エンティティに特定のタイプのコールバックを有効化する。
  • 対応するシグナルを実装する。

コールバックを有効にする方法やコールバックコードを書く方法を学ぶ前に、異なるコールバックタイプとそれらが実行される原因を理解することが重要です。

コールバックタイプ

衝突とトリガーは、物理エンジン内の衝突検出ステップによって生成された二つのエンティティペアまたはエンティティと静的オブジェクトのペアとして始まります。エンティティに取り付けられた物理コンポーネントの組み合わせや、トリガーのプロパティの値(true/false)に応じて、以下の表に示すようなコールバックタイプが実行されます。

エンティティ vs エンティティ

コンポーネント構成に応じて、物理エンジンを搭載したエンティティは次のように分類されます:

  • 非トリガーコライダー:非トリガー物理コライダーを持ち、そのオプションとして運動体物理ボディを持つエンティティ。
  • トリガーコライダー:トリガー物理コライダーを持ち、そのオプションとして運動体物理ボディを持つエンティティ。
  • ダイナミックボディ:非トリガー物理コライダーとダイナミック(非運動体)物理ボディを持つエンティティ。

衝突ペアが二つのエンティティ A と B で構成されている場合、以下のコールバックが実行される可能性があります(各エンティティが属するグループに応じて):

エンティティ A x B 非トリガーコライダー トリガーコライダー ダイナミックボディ
非トリガーコライダー なし OnTrigger OnCollision
トリガーコライダー OnTrigger なし OnTrigger
ダイナミックボディ OnCollision OnTrigger OnCollision

エンティティ vs 静的コライダー

一方、静的コライダーは IsTrigger プロパティに応じてトリガーまたは非トリガーのいずれかになります。
エンティティと静的コライダーから構成される衝突ペアの可能な組み合わせは以下の通りです:

構成(エンティティと静的) 非トリガー静的コライダー トリガー静的コライダー
非トリガーコライダー なし OnTrigger
トリガーコライダー OnTrigger なし
ダイナミックボディ 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
Quantum エンティティプロトタイプの物理プロパティを介して物理コールバックを設定します。

各エンティティには、いくつかのコールバックを有効にできます。

注意: 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

次は、上記とは独立して有効にできる入出力コールバックです:

  • CallbackFlags.OnDynamicCollisionEnter

  • CallbackFlags.OnDynamicCollisionExit

  • CallbackFlags.OnDynamicTriggerEnter

  • CallbackFlags.OnDynamicTriggerExit

  • CallbackFlags.OnStaticCollisionEnter

  • CallbackFlags.OnStaticCollisionExit

  • CallbackFlags.OnStaticTriggerEnter

  • CallbackFlags.OnStaticTriggerExit

エンティティごとにコールバックを有効にする必要があるのは、デフォルトのシミュレーションをできる限り速くするための意図的な設計選択です。また、入出力コールバックは(基本的なコールバックと比較して)若干多くのメモリと CPU 使用率が発生するため、できるだけシンプルなシミュレーションを実現するために、これらは可能な限り避けるべきです。

コールバックシグナル

注意: 衝突およびトリガーコールバックは、エンティティ 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);
  }
}

コールバックに反応するには、アクティブなシステム内の少なくとも1つで対応するシグナルインターフェースを実装してください(無効なシステムではシグナルは実行されません):

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);
    }
}

上記のコードは、シグナル実装時に使用できる別の最適化の例を示しています。SystemSignalsOnly から継承することで、システムはシグナルを扱うことができ、空の更新関数をスケジュールする必要がなくなります(これは不必要にタスクシステムのオーバーヘッドを招きます)。

CollisionInfo

OnCollisionEnter および OnCollision のシグナルは、CollisionInfo 構造体を介して衝突しているエンティティに関する追加情報を提供します。

接触ポイント

接触ポイントに関する情報は、ContactPoints API を介してアクセスできます。

  • Average : すべての接触ポイントの平均
  • Count : 接触ポイントの数
  • Length : すべての接触ポイントを含むバッファ
  • First : 最初の三角形の最初の接触ポイントを返します。

First は、単一または任意の接触ポイントが必要な場合に計算を節約できます。また、平均接触点である必要はありません。

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

C#

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

Sphere-Triangle 衝突には1つの接触ポイントがあるため、AverageContactPoints[0] は同じ接触を返します。その他のタイプの衝突は、より多くの接触ポイントを持つ場合があります。

メッシュ衝突

エンティティがメッシュに衝突するとき、CountAverage は、その特定のメッシュに属するすべての三角形の衝突を考慮します。
エンティティが衝突するたびに三角形ごとにコールバックを受け取るのではなく、これらの衝突三角形は単一の 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