Static Colliders
簡介
向場景添加靜態碰撞體只需三個簡單步驟:
- 將 Quantum 靜態碰撞體腳本附加到 Unity 遊戲物件上;
- 編輯屬性以使其與場景中靜態障礙物的幾何形狀相符;
- 通過 MapData 腳本烘焙場景。
Unity 碰撞體作為來源
Quantum 靜態碰撞體也可以鏡像 Unity 碰撞體的屬性。
為此,只需將所需的碰撞體拖放到 Quantum 靜態碰撞體組件上的Source Collider欄位中:
形狀
2D 物理形狀包括:
- 圓形
- 方形
- 多邊形
注意: 所有這些形狀都有一個Height欄位,可用於創建 2.5D 形狀。
3D 物理形狀包括:
- 球形
- 方盒形
- 網格
配置
靜態碰撞體可以配備PhysicsMaterial和用戶資產。後者可在模擬中通過碰撞回調使用。
平滑球形網格碰撞體
Static Mesh Collider 3D有一個稱為Smooth Sphere Mesh Collision的選項。啟用此選項後,物理求解器將把球形與網格的碰撞視為網格是一個常規的平坦光滑平面來處理。這可以防止球形在與三角形邊緣碰撞時產生旋轉。
運行時啟用 / 禁用
本節將介紹幾種在模擬運行時啟用和禁用靜態碰撞體的方法。
物理引擎
可以直接在物理引擎中在運行時切換靜態碰撞體的開啟和關閉狀態。
要使靜態碰撞體可切換,需要在 編輯時 (Unity 中)設置其Mode並烘焙到Map資產中。模式可以設置為:
Immutable(默認):碰撞體在運行時不能被啟用或禁用。Toggleable Start On:碰撞體在運行時可切換,並且一開始處於 啟用 狀態。Toggleable Start Off:碰撞體在運行時可切換,並且一開始處於 禁用 狀態。
一旦靜態碰撞體被標記為可切換並烘焙後,就可以在模擬(Quantum)運行時使用Frame.Physics3D和Frame.Physics2D中的SetStaticColliderEnabled() 分別啟用和禁用 3D 和 2D 靜態碰撞體。
作為參數傳遞的index是碰撞體在frame.Map.StaticColliders數組中的索引。碰撞回調會在其TriggerInfo或CollisionInfo的StaticData中返回靜態碰撞體的索引(ColliderIndex)。
重要: 禁用的靜態網格碰撞體會被物理查詢忽略,並且不會觸發碰撞信號。
手動跟蹤
儘管可以在物理引擎級別啟用 / 禁用靜態碰撞體,但也有多種方法可以手動執行相同的操作。
為狀態保留一個全局位元集
如果唯一目的是跟蹤在碰撞回調中哪些靜態碰撞體要忽略或考慮,最方便的方法是定義一個全局BitSet,其長度等於或大於frame.Map.StaticColliders數組。這可以作為Frame物件的一部分或作為單例組件來完成。
C#
singleton component StaticColliderState {
bitset[256] colliders;
}
這允許將位集實例與碰撞體索引一起使用來設置其位元。
C#
// loops through the bitset to initialize all bits as "On" to mark all colliders as active
public override void OnInit(Frame frame)
{
var collidersState = frame.Unsafe.GetPointerSingleton<StaticColliderState>();
for (int i = 0; i < collidersState->colliders.Length; i++) {
collidersState->colliders.Set(i);
}
}
public void OnTrigger3D(Frame frame, TriggerInfo3D info)
{
if (info.IsStatic == false) return;
// Use a custom asset slotted in the UserAsset field to identify toggleable colliders
var colliderAsset = frame.FindAsset<MyColliderAsset>(info.StaticData.Asset);
if (colliderAsset == null) return;
var collidersState = frame.Unsafe.GetPointerSingleton<StaticColliderState>();
collidersState->colliders.Clear(info.StaticData.ColliderIndex);
}
然後可以使用IsSet()讀取值,並用於檢查是否應該處理或忽略碰撞信號。這在處理靜態可交互物件、環境屏障或為移動實現IKCCCallbacks3D時特別有用。
用行為切換
靜態碰撞體是資產,也就是說它們在運行時是無狀態且不可變的。然而,在某些情況下,靜態物件應該根據動態條件來啟用 / 禁用。
例如,拾取物通常可以用靜態位置和觸發碰撞體來表示;將它們轉換為靜態碰撞體可以避免與動態實體相關的開銷。不幸的是,通常用於在拾取冷卻時間後重新生成強化道具的計時器需要一個狀態。可以通過擴展上一節中介紹的概念來解決這個難題。
首先,需要在某處保存表示強化道具的靜態碰撞體的狀態。
C#
singleton component PowerUps {
[ExcludeFromPrototype] bitset[256] IsPowerUp;
[ExcludeFromPrototype] bitset[256] State;
[ExcludeFromPrototype] array<FP>[256] Timers;
FP SpawnCooldown;
}
然後可以創建一個系統來處理強化道具的啟用和禁用。
C#
public unsafe class MyPowerUpSystem : SystemMainThread {
public override void OnInit(Frame frame)
{
var powerUps = frame.Unsafe.GetPointerSingleton<PowerUps>();
for (int i = 0; i < powerUps->IsPowerUp.Length; i++)
{
var powerUp = frame.FindAsset<MyPowerUpAsset>(frame.Map.StaticColliders3D[i].StaticData.Asset);
if (powerUp == null) {
powerUps->IsPowerUp.Clear(i);
continue;
}
powerUps->IsPowerUp.Set(i);
powerUps->State.Set(i);
powerUps->Timers[i] = FP._0;
}
}
public override void Update(Frame frame)
{
var powerUps = frame.Unsafe.GetPointerSingleton<PowerUps>();
for (int i = 0; i < powerUps->IsPowerUp.Length; i++)
{
if (powerUps->IsPowerUp.IsSet(i) == false) continue;
if (powerUps->State.IsSet(i)) continue;
powerUps->Timers[i] -= frame.DeltaTime;
if(powerUps->Timers[i] > 0) continue;
powerUps->State.Set(i);
// Other code visualizing the spawned / re-enabled power-up
// can use frame event to trigger VFX, SFX, re-enable visual / GameObject
}
}
public void OnTrigger3D(Frame frame, TriggerInfo3D info)
{
if(info.IsStatic == false) return;
var powerUps = f.Unsafe.GetPointerSingleton<PowerUps>();
if(powerUps->IsPowerUp.IsSet(info.StaticData.ColliderIndex) == false) return;
if(powerUps->State.IsSet(info.StaticData.ColliderIndex) == false) return;
powerUps->State.Clear(info.StaticData.ColliderIndex);
powerUps->Timers[info.StaticData.ColliderIndex] = powerUps->SpawnCooldown;
// Remember to communicate the disabled state visually, e.g. trigger a frame event to disable the GameObject in Unity
}
Back to top