碰撞与触发器:为什么事件从不触发
为什么 OnCollisionEnter 和 OnTriggerEnter 不触发?
两个物体都需要 collider,且其中一个需要 Rigidbody:OnCollisionEnter 要非 kinematic 的那种,OnTriggerEnter 则任何都行(包括 kinematic)。然后再检查方法名和它的参数、脚本是否挂在持有 collider 的那个物体上,以及这两个层在 Layer Collision Matrix 里是否仍互相勾选。
两个物体在屏幕上明明碰到了,你的方法却不执行。Console 里一点动静都没有——没报错、没警告——Unity 压根就没调用过它。原因只有六个,全部排查一遍大约只要一分钟。
第一个问题:这两个事件你到底要哪个?
✓ OnCollisionEnter
solid hits solid
Collision,里面带着接触点和撞击时的速度。✓ OnTriggerEnter
something enters an area
Collider:只有刚进来的那个东西,不附带任何撞击信息。六个排查项,按顺序来
- 1
Rigidbody 得在某一边。只有当物理系统在盯着两个物体中的至少一个时,Unity 才会上报接触。碰撞事件要求至少一侧有一个非 kinematic 的 Rigidbody;trigger 事件只要求至少一侧有 Rigidbody,kinematic 也可以。两个物体都静静躺着,就什么都不会上报。
- 2
方法名和参数。
OnTriggerEnter(Collider other)和OnCollisionEnter(Collision collision)——拼写必须分毫不差。Unity 是按名字找这些方法的,所以写成onTriggerEnter,或者在 3D 场景里用Collider2D,编译毫无问题,但永远不会被调用。 - 3
脚本要挂在正确的 object 上。消息会发给持有该 collider 的 GameObject,以及持有这个 collider 所属 Rigidbody 的 GameObject。脚本挂在这两者之上的任何一层,都什么都听不到。
- 4
两个 collider 都存在且处于启用状态。看 Inspector 最快:按 Play,走过去撞一下那个物体,盯着 Scene 窗口里两个 collider 线框是不是真的相碰。保持默认大小的 collider 往往整个缩在模型内部。
- 5
两个 layer 被允许相遇吗。 里放着 Layer Collision Matrix。那里只要有一个格子没打勾,两个物体就会彻底无视彼此——没有碰撞、没有 trigger、没有任何消息。
- 6
2D 和 3D 永远不会相遇。
Collider2D和Collider是住在同一个场景里的两套独立物理引擎。2D collider 永远碰不到 3D collider,而 2D 的事件是那些结尾带2D的函数。
一个能用的 trigger,和一个能用的碰撞
using UnityEngine;
// The coin sits still, so the Rigidbody lives on the player instead.
[RequireComponent(typeof(Collider))]
public class Coin : MonoBehaviour
{
public int value = 1;
void Reset()
{
// Runs when the component is added: tick Is Trigger for whoever forgets.
GetComponent<Collider>().isTrigger = true;
}
void OnTriggerEnter(Collider other)
{
// Anything can walk in here — check who it was before scoring.
if (!other.CompareTag("Player")) return;
// Wire this into whatever keeps your score.
Debug.Log($"+{value}");
Destroy(gameObject);
}
}using UnityEngine;
[RequireComponent(typeof(Rigidbody), typeof(Collider))]
public class Crate : MonoBehaviour
{
public AudioSource thud;
void OnCollisionEnter(Collision collision)
{
// Collision carries the impact: a light nudge should not sound like a crash.
if (collision.relativeVelocity.magnitude < 2f) return;
thud.Play();
}
}还有两个常踩的坑
CharacterController 不是 Rigidbody。它永远不会调 OnCollisionEnter——取而代之的是 OnControllerColliderHit——但它移动时照样会触发 trigger。所以才会有“角色推不动箱子、却捡得起金币”这种怪事。
速度足够快的东西会在两帧之间径直穿过一面薄墙。给那个 Rigidbody 把 Collision Detection 设成 Continuous,顺便把墙也加厚一点。
直接改写 transform.position 来移动 Rigidbody 等于瞬移,而瞬移不会留下任何一条路径给物理去检测。改用 Rigidbody.MovePosition 或用速度来移动,接触就会回来。
做出这个的那一课
弹道、伤害与特效
这篇指南单独成篇,是一份可以直接照着做的配方。在课程里,同样的东西会作为贯穿全部五个章的那个项目的一部分来搭建 —— 角色与战斗,第 05 课。