Unity 碰撞事件沒反應的話怎麼處理?

不知道大家有沒有遇到過 Unity Collision 事件無法觸發的情況?若有的話,以下是我所使用的解法,歡迎繼續看下去。

確認碰撞設定

查看 Collider 的 Layer 設定

查看物件的 Layer 設定

到 Edit > Project Settings

Physics 查看 Physics 設定 (若是使用 2D 的 Collider 請到 Physics2D)

滑到最下面會看到打勾的地方,確認兩個 Layer 交集的地方是否有打勾,若沒有則不會有反應

像我若把 Default / Default 的勾取消,兩個就不會碰撞了

2.拉大 Collider 範圍

可以調整 Collider Size 的部分,這邊我做了不好的示範動了 Scale。動了 Scale 有時候會出 BUG

3. 確認物件是否有 Rigidbody

這點自己也常常遺忘,要確認兩個物件都有,才會觸發!

4. 若真的偵測不到可以用 Raycast

我們先新增一個 C# Script

這邊示範叫 RaycastController

新增一個變數叫 previousPosition,這邊我們用 SerializeField 讓它顯示在面板上,方便檢查是否有變更

[SerializeField]
private Vector3 previousPosition = Vector3.zero;


在 Start() 的時候,給予 previousPosition 現在的位置資訊

private void Start()
{
    previousPosition = transform.position;
}


在 FixedUpdate() 中放入以下程式

void FixedUpdate()
{
    // Bit shift the index of the layer (8) to get a bit mask
    int layerMask = 1 << 8;

    // This would cast rays only against colliders in layer 8.
    // But instead we want to collide against everything except layer 8. The ~ operator does this, it inverts a bitmask.
    layerMask = ~layerMask;

    float distance = Vector3.Distance(previousPosition, transform.position);
    Vector3 direction = (previousPosition - transform.position).normalized;

    RaycastHit hit;
    // Does the ray intersect any objects excluding the player layer
    if (Physics.Raycast(transform.position, direction, out hit, distance, layerMask))
    {
        Debug.DrawRay(transform.position, direction * hit.distance, Color.yellow);
        Debug.Log("Did Hit");
    }
    else
    {
        Debug.DrawRay(transform.position, direction * 1000, Color.white);
    }

    previousPosition = transform.position;
}


下面這一段程式是設定 Raycast(射線) 會對哪個 Layer 有反應。目前是設定除了 Layer 8 以外都可以有反應

// Bit shift the index of the layer (8) to get a bit mask
int layerMask = 1 << 8;

// This would cast rays only against colliders in layer 8.
// But instead we want to collide against everything except layer 8. The ~ operator does this, it inverts a bitmask.
layerMask = ~layerMask;

再來我們先設定射線的長度

float distance = Vector3.Distance(previousPosition, transform.position);

再定義射線的方向,這邊就用前一個位置減掉現在的位置。normalized 是把值都變成 0~1 之間,假如有一個向量 (2,1,0) 他會變成 (0.89, 0.45, 0.00) (背後的運算不清楚 QQ)

Vector3 direction = (previousPosition - transform.position).normalized;

宣告 RaycastHit,這個是 Unity Raycast 打到物件後,會把資訊存在這個變數

RaycastHit hit;

這邊就是重頭戲了,Raycast 的部分

// Does the ray intersect any objects excluding the player layer
if (Physics.Raycast(transform.position, direction, out hit, distance, layerMask))
{
    Debug.DrawRay(transform.position, direction * hit.distance, Color.yellow);
    Debug.Log("Did Hit");
}
else
{
    Debug.DrawRay(transform.position, direction * 1000, Color.white);
}

我是使用文件上這條的用法

public static bool Raycast(Vector3 originVector3 direction, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);

  • origin:我們使用現在物件的位置
  • direction:我們使用剛剛計算的 direction
  • maxDistance:我們使用剛剛計算的 distance
  • layerMask:使用剛剛宣告的除了 Layer 8 以外的 LayerMask
  • queryTriggerInteraction:不用輸入,因為有預設值

Debug.DrawRay 是用來視覺化射線的長度,可用可不用。這樣我們就完成了射線版的Collider。

為什麼 TriggerEnter/CollisionEnter 有時候會偵測不到?

原因是上一個 frame 和下一個 frame 物件的位置可能剛好飛過 Collider。若用射線在前一個位置和後一個位置連成一條線就可以避免沒偵測到的問題了

留言