This document is about: QUANTUM 2
SWITCH TO

Static Colliders

概述

新增靜態碰撞器到一個場景,需要進行三個簡單的步驟:

  1. 附加一個Quantum靜態碰撞器指令碼到一個Unity遊戲物件;
  2. 編輯屬性,使其與場景中的靜態障礙物所需的幾何體相似;及,
  3. 透過地圖資料指令碼來嵌入場景。
Step 1 & 2 - Add Static Colliders to GameObject in Unity Scene and adjust Settings
第1及2步——新增靜態碰撞器到Unity場景中的遊戲物件,並且調整設定。
Step 3 - Baking the Map Saves the Scene Colliders as a Quantum Asset (Map)
第3步——嵌入地圖,將場景碰撞器儲存為一個Quantum資產(地圖)。

Unity碰撞器作為一個來源

Quantum靜態碰撞器也可以從一個Unity碰撞器來鏡像屬性。
為了做到這點,簡單地拖放所需的碰撞器到Quantum靜態碰撞器元件上的Source Collider欄位之中:

unity-collider-source

形狀

2D物理形狀是:

  • 圓形
  • 方形
  • 多邊形

請注意: 所有這些形狀都有一個Height欄位,其允許建立2.5D形狀。

3D物理形狀是:

  • 球體
  • 長方體
  • 網格

設定

靜態碰撞器可以適配一個 物理材質 及一個 使用者資產。後者透過碰撞回調在模擬中可用。

平順球體網格碰撞器

一個Static Mesh Collider 3D有一個選項稱為Smooth Sphere Mesh Collision。當切換這個選項為開,物理求解將處理球體網格碰撞,如同網格是一個規則平面及平滑平面一樣。這防止了新增旋轉到一個與三角形邊碰撞的球體。

Static Smooth Mesh Collider
靜態網格碰撞器。
如果`Static Mesh Collider 3D`以`Smooth Sphere Mesh Collision`選項被標記,但網格不是完全地平坦,它可能導致意料之外的碰撞回應。

在運行階段啟用/停用

本章節將呈現多個方法以在模擬中在運行階段時啟用及停用靜態碰撞器。

物理引擎

可以直接在物理引擎中,在運行階段時切換靜態碰撞器為開或關。

要使一個靜態碰撞器可切換,其Mode需要被設定為_編輯時間_ (Unity),並且嵌入到Map資產中。模式可被設定為:

  • Immutable(預設):碰撞器在運行階段無法被啟用或停用。
  • Toggleable Start On:碰撞器在運行階段可以被切換,並且開始為 啟用
  • Toggleable Start Off:碰撞器在運行階段可以被切換,並且開始為 停用
Enable toggle on 3D Static Mesh Colliders
在一個3D靜態網格碰撞器元件上啟用切換。

當一個靜態碰撞器已經被標記為可切換及已嵌入,可以在運行階段使用Frame.Physics3DFrame.Physics2D中(分別用於3D及2D靜態碰撞器)的SetStaticColliderEnabled(),從模擬(Quantum)啟用和停用碰撞器。

需要作為參數傳送的index是在frame.Map.StaticColliders陣列中的碰撞器的索引。碰撞回調傳回一個靜態碰撞器的索引(ColliderIndex),作為它們的TriggerInfoCollisionInfo中的StaticData的一部分。

重要事項: 一個停用的靜態網格碰撞器被物理查詢忽略,並且將不會觸發碰撞信號。

手動追蹤

雖然靜態碰撞器可以在物理引擎階層被啟用/停用,有各種方法可以手動地做相同的事情。

針對狀態保有一個全域位元設定

如果唯一的目標是追蹤在一個碰撞回調中要忽略或考量哪些靜態碰撞器,最方便的方式是定義一個全域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 f)
{
    var collidersState = f.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 f)
{
    var powerUps = f.Unsafe.GetPointerSingleton<PowerUps>();
    for (int i = 0; i < powerUps->IsPowerUp.Length; i++)
    {
        var powerUp = f.FindAsset<MyPowerUpAsset>(f.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 f){
    var powerUps = f.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] -= f.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