Collision Events
Ba callback vật lý — OnCollisionEnter, Stay và Exit — báo chi tiết va chạm: điểm tiếp xúc, lực tác động, vận tốc tương đối và Collider của đối phương.
Hãy tưởng tượng...
Collision Events giống như bảo hiểm tai nạn. Khi hai xe đâm nhau (OnCollisionEnter), bảo hiểm ghi lại: ai đâm ai, tốc độ bao nhiêu, điểm đâm chỗ nào. Trong khi xe còn chạm nhau (OnCollisionStay), vẫn tiếp tục ghi nhận. Khi tách ra (OnCollisionExit), đóng hồ sơ. Thông tin trong `Collision` object giống như báo cáo tai nạn — bạn có thể đọc lực va chạm, toạ độ va chạm, và "ai" gây ra nó.
Khái niệm chi tiết
Khi hai Collider va chạm trong Unity Physics (không phải Trigger), engine gọi ba phương thức
callback: OnCollisionEnter(Collision) vào frame đầu tiên,
OnCollisionStay(Collision) mỗi physics frame tiếp theo, và
OnCollisionExit(Collision) khi tách ra. Tham số Collision chứa toàn
bộ dữ liệu va chạm.
Object Collision cung cấp: collision.gameObject (object đâm vào),
collision.contacts (mảng điểm tiếp xúc bao gồm vị trí và normal vector),
collision.relativeVelocity (vận tốc tương đối — thường dùng để tính sức mạnh va
chạm), và collision.impulse (xung lực tổng).
Callback này được gọi trong Physics thread, sau bước
tính toán vật lý. Vì vậy, bạn không nên thực hiện các thao tác nặng (tạo nhiều object, tải
asset) trong đây — thay vào đó, chỉ cập nhật trạng thái (biến bool, queue event) và xử lý logic
nặng ở Update. OnCollisionStay đặc biệt tốn kém nếu có nhiều object đang đứng yên
chạm nhau — có thể tối ưu bằng Physics.reuseCollisionCallbacks = true.
Để nhận callback, cả hai object tham gia va chạm phải có Collider, và ít nhất một phải có Rigidbody (non-kinematic). Static Collider (không Rigidbody) có thể nhận callback từ Dynamic Rigidbody đâm vào, nhưng hai Static Collider không bao giờ báo cáo collision với nhau.
Dữ liệu trong Collision Object
Collision collision
Hướng dẫn thực hành
Thêm phương thức callback vào MonoBehaviour
OnCollisionEnter(Collision collision) — Unity tự gọi khi va chạm, không cần đăng ký event.
Đọc collision.relativeVelocity.magnitude
Dùng để phán đoán sức mạnh va chạm: nhẹ → tiếng kêu nhỏ, mạnh → gây damage, vỡ vụn...
Lấy điểm tiếp xúc đầu tiên
collision.GetContact(0) hoặc collision.contacts[0].point — dùng để spawn particle, decal tại đúng vị trí va chạm.
Lọc theo tag hoặc layer
collision.gameObject.CompareTag("Enemy") trước khi xử lý logic — tránh phản ứng nhầm với mọi va chạm.
Tránh lạm dụng OnCollisionStay
Nếu chỉ cần biết "đang chạm", dùng biến bool set trong Enter/Exit, rồi đọc trong Update thay vì Stay.
Trình mô phỏng tương tác
Click "Bắn" để phóng projectile vào tường — xem các sự kiện collision được ghi lại theo thứ tự.
Nhấn "Bắn" để xem collision events...
Ví dụ Code
Cơ bảnPhát âm thanh và spawn particle tại điểm va chạm.
using UnityEngine;
public class CollisionSounds : MonoBehaviour
{
public AudioClip softHit, hardHit;
public ParticleSystem impactFX;
public float hardHitThreshold = 5f;
void OnCollisionEnter(Collision collision)
{
float speed = collision.relativeVelocity.magnitude;
// Chọn âm thanh dựa vào tốc độ va chạm
AudioClip clip = speed > hardHitThreshold ? hardHit : softHit;
AudioSource.PlayClipAtPoint(clip, collision.GetContact(0).point);
// Spawn hiệu ứng tại điểm va chạm đầu tiên
if (impactFX != null)
{
ContactPoint contact = collision.GetContact(0);
Instantiate(impactFX, contact.point,
Quaternion.LookRotation(contact.normal));
}
}
}Ví dụ Code
Nâng caoVật thể vỡ vụn khi va chạm đủ mạnh — spawn mảnh vỡ và xoá original.
using UnityEngine;
public class BreakableObject : MonoBehaviour
{
public GameObject[] fragmentPrefabs; // Các mảnh vỡ
public float breakForceThreshold = 8f;
public float fragmentExplosionForce = 200f;
bool broken = false;
void OnCollisionEnter(Collision collision)
{
if (broken) return;
if (collision.relativeVelocity.magnitude < breakForceThreshold) return;
broken = true;
ContactPoint contact = collision.GetContact(0);
// Spawn và phân tán mảnh vỡ
foreach (GameObject frag in fragmentPrefabs)
{
GameObject piece = Instantiate(frag, transform.position,
Random.rotation);
Rigidbody rb = piece.GetComponent<Rigidbody>();
if (rb != null)
rb.AddExplosionForce(fragmentExplosionForce,
contact.point, 1f);
}
Destroy(gameObject);
}
}📌 Ghi nhớ nhanh
- ▸Enter → Stay → Exit, đúng theo thứ tự
- ▸relativeVelocity.magnitude = sức mạnh va chạm
- ▸contacts[0].point = vị trí va chạm trong world
- ▸contacts[0].normal = hướng bề mặt bị chạm
- ▸Static-static không bao giờ gọi OnCollisionEnter
⚠️ Lỗi thường gặp
❌ Chỉnh physics trong OnCollisionStay mỗi frame
Gây dao động không ổn định (jitter) và giật lag
✅ Chỉ dùng Enter/Exit để set cờ, xử lý logic trong FixedUpdate
❌ Truy cập contacts[] bằng index không kiểm tra
contacts có thể rỗng, gây IndexOutOfRange exception
✅ Dùng GetContact(0) hoặc kiểm tra contacts.Length > 0