iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
自我挑戰組

Unity 基本功能實作與日常紀錄系列 第 11

Day11: Understand Collision and Trigger

  • 分享至 

  • xImage
  •  

我們在環境中判斷物件是否碰撞的時候都會使用到碰撞器(Collider)與剛體(Rigidbody),畢竟剛體可以使物件在環境中在物理影響下移動。碰撞體必須要加上剛體才能讓雙方產生碰撞。具有剛體 Rigidbody 才能產生物理的效應。但這邊我因為不熟悉 Collision 與 Rigidbody 之間的關係,希望透過這個文章來更加理解該使用的方式。

產生碰撞的必要條件是兩個都要有 Collider,但雙方Component 中必須要有一個包含Rigidbody。在今天的實驗我們會說明,OnCollisionEnter, OnCollisionExit, OnCollisionStay 三者的差異,以及 OnTriggerEnter, OnTriggerExit, OnTriggerStay三者的差異。

建置環境

  1. 先建置該兩個物件,我這邊新增一個 UpObj 也就是上面的圓球,下面 DownObj 下面是藍色的長方形方塊。

  2. 接下來是各個宣告 Collider 的新增。UpObj 包含 Rigidbody, Sphere Collider。 DownObj 僅包含 Box Collider。請記住兩個Rigidbody 內 IsTrigger 都不要勾選。

  3. 這邊補充如果點選 Collider 中的 Edit Collider,可以在Scene中有效的調整該 Collider 的大小。更加的方便微調該Collider 大小

  4. 新增一個文本到 UpObj ,取名為CollisionManagement.cs,主要是要偵測其他物件的 Trigger 與 Collision ,對自己的Collision, Trigger 是沒有反應的。就是說若你要觸發別人的Collision 或 Trigger 就需要將文本放進主要移動的物件上。

OnCollisionEnter, OnCollisionExit, OnCollisionStay 三著的差異

  1. 我們這邊撰寫一個程式碼針對該三者的差異,針對在不同的狀態下讓接觸圓球去顯示不同的顏色。這邊在剛接觸(Enter)的時候仍為紅色,貼著方塊移動時為藍色同時轉動該下方的方塊旋轉,離開方塊表面後圓球變成綠色。

  2. OnCollisionEnter 當物件接觸表面時

private void OnCollisionEnter(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Enter!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.red);
        }
    }
  1. OnCollisionExit 當物件離開表面時
private void OnCollisionExit(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Exit!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.green);
        }
    }
  1. OnCollisionStay 當物件貼合在表面時,同時慢慢地旋轉下方的方塊(Z軸反向翻轉)
private void OnCollisionStay(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Stay");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
            collision.transform.localEulerAngles  += new Vector3(0, 0, -10) * Time.deltaTime;
        }
    }
  1. 整個Collision 控制的程式碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionManagement : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Enter!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.red);
        }
    }

    private void OnCollisionExit(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Exit!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.green);
        }
    }

    private void OnCollisionStay(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Stay");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
            collision.transform.localEulerAngles  += new Vector3(0, 0, -10) * Time.deltaTime;   
        }
    }
}
  1. 回到 Unity 執行結果如下
  • 當物體掉落下的時候為紅色,瞬間接觸(OnCollisionEnter)的時候也會是紅色。

  • 接觸貼合的時候為藍色 OnCollisionStay

  • 離開表面的時候就會變成綠色 OnCollisionExit

  1. Console 的顯示結果如下,可以看到出現資訊的次數來觀察該結果。

OnTriggerEnter, OnTriggerExit, OnTriggerStay三著的差異

  1. 首先我們點選該其中一個物件上的 IsTrigger,兩個勾選也不要緊。在這我僅勾選 UpObj 上的 IsTrigger,Sphere Collider內部的 Is Trigger 。

  2. 當 IsTrigger 點選後就會讓兩個物件無法產生碰撞的物理行為。兩物件接觸不會像剛剛一樣會接觸產生物理的反應,而是會穿透。我們可以想像IsTrigger 就僅是知道兩物件是否接觸,但互相不會有碰撞的影響。如下這裡UpObj 穿透 DownObj。

  3. 這邊我們撰寫程式,如下

  • 首先圓球到接觸表面的瞬間
private void OnTriggerEnter(Collider collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Enter!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.red);
        }
    }
  • 當圓球離開方塊物件時
private void OnTriggerExit(Collider collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Exit!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.green);
        }
    }
  • 接著當圓球穿透方塊時
private void OnTriggerStay(Collider collision) 
    {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Stay!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
        }
    }
  1. 整個程式碼如下,可以注意到都是判斷觸發條件的物件是 DownObj,也就是被觸發的物件這樣
private void OnTriggerEnter(Collider collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Enter!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.red);
        }
    }
    private void OnTriggerExit(Collider collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Exit!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.green);
        }
    }
    private void OnTriggerStay(Collider collision) 
    {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Stay!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
        }
    }
  1. 下的程式碼完整結果如下,可以看到還是有剛剛 Collider 撰寫的程式碼,等等看會不會執行。就可以知道說哪個判斷的優先權比較高。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionManagement : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Enter!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.red);
        }
    }

    private void OnCollisionExit(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Exit!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.green);
        }
    }

    private void OnCollisionStay(Collision collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Collision Stay!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
            collision.transform.localEulerAngles  += new Vector3(0, 0, -10) * Time.deltaTime;
        }
    }

    private void OnTriggerEnter(Collider collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Enter!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.red);
        }
    }
    private void OnTriggerExit(Collider collision) {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Exit!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.green);
        }
    }
    private void OnTriggerStay(Collider collision) 
    {
        if(collision.gameObject.name == "DownObj")
        {
            Debug.Log("Trigger Stay!");
            gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color.blue);
        }
    }
    
}
  1. 回到 Unity 後我們調整一下該 Rigidbody 上的 Drag ,調整為 3,讓他掉下來慢一點我們好觀察該改變的狀態。

  2. 執行後結果如下

  • 一開始圓球仍是紅色的

  • 接下來穿透時會變成藍色的

  • 離開被穿透的物件後

  • 結果從 Console可以看出該三個狀態

結論

  1. 當我們啟用 IsTrigger (true)時候,Collider 會被系統所忽略,不會產生任何碰撞效果,可以使用 OnTriggerEnter, Stay, Exit。
  2. 當我們啟用 IsTrigger (false)時候,Collider 會被系統使用,會產生物理性的碰撞,可以使用 OnCollisionEnter, Stay, Exit。
  3. 碰撞器(Collider) 我們可以使用在兩物體的撞擊或是物體掉在地面上彈起的效果。
  4. 觸發器(IsTrigger) 在靠近某個物體的時候讓其他物體產生反應,比如自動門之類的可預測物體。

上一篇
Day10: Image Progress Bar
下一篇
Day12: Use Lerp move A to B by Time or Speed!
系列文
Unity 基本功能實作與日常紀錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言