This document is about: QUANTUM 2
SWITCH TO

Callbacks

概述

Quantum中的碰撞及觸發回調透過 系統信號 被處理。讓回調針對任何特定實體來被執行,需要兩個步驟:

  • 啟用針對實體的回調的特定類型。
  • 執行相應的信號。

在學習如何啟用回調及如何撰寫程式碼之前,重要的是先了解不同的回調類型及哪些事情讓它們被執行。

回調類型

碰撞及觸發由兩個實體,或由物理引擎中的碰撞偵測步驟生成的一個實體靜態對來啟動。取決於附加於實體的物理元件的組合,及觸發屬性的值(真/偽),下表描述了將被執行的回調的類型。

實體 vs 實體

根據它們的元件組合,物理啟用的實體可分類如下:

  • 非觸發碰撞器:有一個非觸發物理碰撞器及,可選地,一個運動學的物理主體。
  • 觸發碰撞器:有一個觸發物理碰撞器及,可選地,一個運動學的物理主體。
  • 動態主體:有一個非觸發物理碰撞器及一個動態(非運動學的)物理主體的實體。

當一個碰撞對由兩個實體A及B組成,這些可能被執行的回調(取決於各個實體屬於的群):

實體 A x B 非觸發碰撞器 觸發碰撞器 動態主體
非觸發碰撞器 觸發時 碰撞時
觸發碰撞器 觸發時 觸發時
動態主體 碰撞時 觸發時 碰撞時

實體 vs 靜態碰撞器

另一方面,靜態碰撞器可以是觸發或非觸發,這是根據它們的IsTrigger屬性。 當碰撞對由一個實體及一個靜態碰撞器組成,這些是可能的結合:

元件(實體及靜態) 非觸發靜態碰撞器 觸發靜態碰撞器
非觸發碰撞器 觸發時
觸發碰撞器 觸發時
動態主體 碰撞時 觸發時

在實體上啟用回調

針對各個個別的實體,可以控制啟用哪個回調類型(以及針對哪個種類的其他碰撞器)。透過Unity中的 實體原型 或物理引擎API中的 設定回調 功能的程式碼來完成,其使用實體及一個碰撞回調旗標。

透過實體原型

在任何帶有一個 物理碰撞器 (2D/3D)的 實體原型 上可以設定物理回調。

setting physics callbacks via the entity prototype's physics properties in the unity editor
透過Unity編輯器中的實體原型的物理屬性來設定物理回調。

各個實體可以有多個回調。

請注意: 在一個 實體原型 上啟用回調,只針對該特定實體 設定回調。您在程式碼中仍然 必須執行 相應的 信號。請見以下 回調信號 的章節以取得更多資訊。

透過程式碼

回調旗標是一個位元遮罩,並且可以使用位元運算來指定多個回調類型,如同以下的例子。

以下程式碼片段啟用了針對另一個動態實體(動態觸發時、動態觸發時動態觸發進入時,及動態觸發結束時)的觸發時回調的完整集合。

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

上述程式碼展示了執行信號時可以使用的另一種最佳化。繼承 僅系統信號 可以讓系統在處理信號的同時不需要排程一個空的更新函數(其將不必要地導致工作系統額外負荷)。

碰撞資訊

針對OnCollisionEnterOnCollision的信號透過CollisionInfo架構提供關於碰撞實體的額外的資訊。

聯絡點

聯絡點上的資訊可以透過ContactPoints API來存取。

  • Average:所有聯絡點的平均
  • Count:聯絡點的數量
  • Length:所有聯絡點的一個緩衝
  • First : 傳回第一個三角形的第一個聯絡點

如果只需要一個/任何聯絡點,並且它不需要是一個已平均的聯絡點,那麼First可以幫助節省運算。

ContactPoints也是一個迭代。當碰撞一個網格,它可用於迭代所有三角形碰撞聯絡點。

C#

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

球體-三角形 碰撞有一個單一聯絡點;因此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);
  }
}

雜項及問與答

使用碰撞回調時的有用資訊:

  • 如果兩個實體有相同的回調集合,回調將被調用兩次——每個其關聯的實體一次。兩個調用的唯一不同是針對 實體其他 的值,其將被交換。
  • 有著觸發及靜態碰撞器的碰撞,不運算正常/點/滲透資料。
  • Unity上的靜態碰撞器可以有一個Quantum DB資產附加到它們的「資產」欄位。投射它到您期待的自訂資產類型,是一個好的方式以新增任意資料到您的靜態碰撞回調。
Back to top