U
09 / 22 · 9 phút
Hỏi đáp

Va chạm và trigger: vì sao sự kiện không chạy

câu trả lời ngắn

Vì sao OnCollisionEnter và OnTriggerEnter không chạy?

Cả hai vật đều cần collider, và một trong hai cần Rigidbody: loại không kinematic cho OnCollisionEnter, loại nào cũng được — kể cả kinematic — cho OnTriggerEnter. Rồi kiểm tên hàm và tham số của nó, script có nằm đúng object chứa collider không, và hai layer còn được tick trong Layer Collision Matrix không.

Hai vật chạm nhau ngay trên màn hình mà hàm của bạn không chạy. Console không báo gì, không lỗi, không cảnh báo — đơn giản là Unity chưa từng gọi nó. Chỉ có sáu lý do, và bạn dò hết chúng trong khoảng một phút.

Trước hết: bạn cần cái nào trong hai?

✓ OnCollisionEnter

solid hits solid

Hai vật không xuyên qua nhau được. Dùng khi cú chạm là va đập thật: một cái thùng bị hất đổ, quả bóng dội tường. Unity đưa cho bạn một Collision, trong đó có các điểm chạm và vận tốc lúc đâm vào.

✓ OnTriggerEnter

something enters an area

Hai vật xuyên thẳng qua nhau, bạn chỉ được báo là chuyện đó vừa xảy ra. Dùng cho đồng xu, cửa ra vào, vùng gây sát thương, tầm với của thanh kiếm. Unity đưa cho bạn một Collider: thứ vừa đi vào, không kèm thông tin va đập.

Sáu thứ cần kiểm, theo thứ tự

  1. 1

    Phải có Rigidbody ở đâu đó. Unity chỉ báo có chạm khi vật lý đang theo dõi một trong hai vật. Sự kiện va chạm cần một Rigidbody không kinematic ở ít nhất một bên; sự kiện trigger cần một Rigidbody ở ít nhất một bên, và kinematic cũng được. Hai vật cùng nằm im thì chẳng báo gì cả.

  2. 2

    Tên hàm và tham số. OnTriggerEnter(Collider other) và OnCollisionEnter(Collision collision) — viết đúng y như vậy. Unity tìm hàm theo tên, nên onTriggerEnter, hay một Collider2D trong cảnh 3D, vẫn biên dịch ngon lành và không bao giờ được gọi.

  3. 3

    Script nằm đúng object. Thông điệp được gửi tới GameObject đang giữ collider, và tới GameObject đang giữ Rigidbody mà collider đó thuộc về. Script nằm cao hơn cả hai chỗ đó thì không nghe thấy gì.

  4. 4

    Cả hai collider đều tồn tại và đang bật. Nhìn Inspector là nhanh nhất: bấm Play, đi vào vật đó, rồi xem hai khung collider có thật sự chạm nhau trong cửa sổ Scene không. Collider để nguyên kích thước mặc định thường nằm lọt thỏm trong model.

  5. 5

    Hai layer được phép gặp nhau. Edit▸Project Settings▸Physics có bảng Layer Collision Matrix. Chỉ cần một ô bị bỏ tick là hai vật phớt lờ nhau hoàn toàn — không va chạm, không trigger, không thông điệp nào.

  6. 6

    2D và 3D không bao giờ gặp nhau. Collider2D và Collider là hai bộ máy vật lý riêng biệt sống chung một cảnh. Collider 2D không bao giờ chạm được collider 3D, và sự kiện của 2D là những hàm có đuôi 2D.

Một trigger chạy được, và một va chạm chạy được

Coin.cs — on the coin: a trigger collider, no Rigidbody needed on the coin itself
using UnityEngine;

// Đồng xu nằm im, nên Rigidbody nằm ở nhân vật chứ không ở đây.
[RequireComponent(typeof(Collider))]
public class Coin : MonoBehaviour
{
    public int value = 1;

    void Reset()
    {
        // Chạy lúc vừa gắn component: tự tick Is Trigger hộ người hay quên.
        GetComponent<Collider>().isTrigger = true;
    }

    void OnTriggerEnter(Collider other)
    {
        // Ai cũng có thể đi vào đây — xem là ai rồi hẵng cộng điểm.
        if (!other.CompareTag("Player")) return;

        // Nối chỗ này vào thứ đang giữ điểm của bạn.
        Debug.Log($"+{value}");
        Destroy(gameObject);
    }
}
Crate.cs — on the crate: a solid collider and a Rigidbody that physics may push
using UnityEngine;

[RequireComponent(typeof(Rigidbody), typeof(Collider))]
public class Crate : MonoBehaviour
{
    public AudioSource thud;

    void OnCollisionEnter(Collision collision)
    {
        // Collision mang theo lực va: chạm nhẹ thì đừng kêu như tông xe.
        if (collision.relativeVelocity.magnitude < 2f) return;

        thud.Play();
    }
}

Hai thứ nữa hay làm người ta mắc

  • CharacterController không phải Rigidbody. Nó không bao giờ gọi OnCollisionEnter — nó gọi OnControllerColliderHit — nhưng nó vẫn kích hoạt trigger khi di chuyển, nên mới có chuyện nhân vật không đẩy nổi cái thùng mà vẫn nhặt được đồng xu.

  • Vật đủ nhanh sẽ nhảy thẳng qua một bức tường mỏng giữa hai khung hình. Trên Rigidbody đó, đặt Collision Detection thành Continuous, và tiện tay cho bức tường dày lên một chút.

  • Di chuyển Rigidbody bằng cách ghi thẳng transform.position là dịch chuyển tức thời, mà dịch chuyển tức thời thì không có đường đi nào cho vật lý kiểm tra. Hãy dùng Rigidbody.MovePosition hoặc dùng vận tốc, các cú chạm sẽ trở lại.

bài học làm ra thứ này

Đạn, sát thương và hiệu ứng

Bài này là một công thức đứng riêng. Trong khoá học, cùng thứ đó được dựng như một phần của dự án xuyên suốt năm tab — Nhân vật & Chiến đấu, bài 05.