Collision Events
Unity 三个物理回调——OnCollisionEnter、Stay、Exit——报告接触点、冲击力、相对速度以及对方 Collider。它们配合 Rigidbody 使用,适合伤害结算、脚步音效、撞击反馈等玩法;理解与 Trigger 事件的差异,能帮程序选对回调路径并控制物理回调带来的性能开销。
想象一下...
Collision Events 就像事故保险报告。两车相撞时(OnCollisionEnter),报告记下谁撞谁、多快、撞在哪。继续接触时(OnCollisionStay)持续记录。分开时(OnCollisionExit)结案。`Collision` 对象就是那份报告——冲击力、世界空间接触点,以及是谁造成的。
概念详解
当两个 Collider 在 Unity Physics 中发生碰撞(非 Trigger)时,引擎触发三个回调:
第一帧的 OnCollisionEnter(Collision)、
之后每个物理帧的 OnCollisionStay(Collision),以及
分离时的 OnCollisionExit(Collision)。Collision 参数
携带完整的接触数据。
Collision 提供:collision.gameObject(对方物体)、
collision.contacts(带位置与法线的接触点)、
collision.relativeVelocity(相对速度——通常当作冲击强度),
以及 collision.impulse(总冲量)。
这些回调跑在物理步、求解器之后。不要在这里大量生成对象或加载资源——
设一个标志或入队事件,把重活放到 Update。许多刚体互相靠着时,
OnCollisionStay 尤其昂贵——可考虑
Physics.reuseCollisionCallbacks = true。
双方都需要 Collider,且至少一方需要非 kinematic 的 Rigidbody。静态 Collider 可从撞来的 Dynamic Rigidbody 收到回调,但两个静态 Collider 永远不会互相报告碰撞。
Collision 对象里的数据
Collision collision
动手步骤
在 MonoBehaviour 中写回调
OnCollisionEnter(Collision collision) —— Unity 自动调用,无需注册事件。
读取 collision.relativeVelocity.magnitude
当作冲击强度:轻 → 轻敲声,重 → 伤害、碎裂……
取第一个接触点
collision.GetContact(0) 或 collision.contacts[0].point —— 在精确命中处生成粒子或贴花。
按 Tag 或 Layer 过滤
先 collision.gameObject.CompareTag("Enemy") 再跑逻辑——忽略无关碰撞。
不要滥用 OnCollisionStay
若只需“是否接触”,在 Enter/Exit 设 bool,在 Update 里读,而不是用 Stay。
交互模拟器
点击 "Fire" 把弹体射向墙壁——按顺序观察碰撞事件日志。
按下“发射”查看 collision events...
代码示例
基础在接触点播放音效并生成粒子。
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;
// Pick a clip from impact speed
AudioClip clip = speed > hardHitThreshold ? hardHit : softHit;
AudioSource.PlayClipAtPoint(clip, collision.GetContact(0).point);
// Spawn FX at the first contact
if (impactFX != null)
{
ContactPoint contact = collision.GetContact(0);
Instantiate(impactFX, contact.point,
Quaternion.LookRotation(contact.normal));
}
}
}代码示例
进阶冲击够强时碎裂——生成碎片并销毁原物体。
using UnityEngine;
public class BreakableObject : MonoBehaviour
{
public GameObject[] fragmentPrefabs; // Debris pieces
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 and scatter fragments
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);
}
}📌 快速记忆
- ▸Enter → Stay → Exit,顺序固定
- ▸relativeVelocity.magnitude = 冲击强度
- ▸contacts[0].point = 世界空间命中位置
- ▸contacts[0].normal = 命中处表面方向
- ▸静态对静态永远不会调用 OnCollisionEnter
⚠️ 常见错误
❌ 在 OnCollisionStay 里每帧改物理
不稳定的抖动与卡顿
✅ 在 Enter/Exit 设标志,在 FixedUpdate 跑逻辑
❌ 不检查长度就索引 contacts[]
contacts 可能为空 → IndexOutOfRange
✅ 用 GetContact(0) 或先检查 contacts.Length > 0