Unity 碰撞检测全面详解
碰撞检测是 Unity 物理系统的核心功能,用于检测和处理游戏对象之间的交互。下面我将从基础到高级全面解析 Unity 的碰撞检测机制。
一、碰撞检测基础组件
1. 碰撞体 (Collider)
-
作用:定义物体的物理形状
-
常见类型:
-
基本碰撞体:Box、Sphere、Capsule
-
网格碰撞体:MeshCollider(精确但性能开销大)
-
复合碰撞体:多个简单碰撞体组合
-
2D专用:BoxCollider2D、CircleCollider2D等
-
2. 刚体 (Rigidbody)
-
作用:使物体受物理引擎控制
-
重要属性:
-
Mass:质量
-
Drag:空气阻力
-
Angular Drag:旋转阻力
-
Use Gravity:是否受重力
-
Is Kinematic:是否仅通过脚本控制
-
二、碰撞检测与触发检测
1. 物理碰撞检测
// 碰撞开始
void OnCollisionEnter(Collision collision) {
ContactPoint contact = collision.contacts[0];
Debug.Log($"与 {collision.gameObject.name} 碰撞,接触点: {contact.point}");
}
// 碰撞持续中
void OnCollisionStay(Collision collision) {
// 每帧调用
}
// 碰撞结束
void OnCollisionExit(Collision collision) {
Debug.Log($"与 {collision.gameObject.name} 结束碰撞");
}
2. 触发检测
// 进入触发区域
void OnTriggerEnter(Collider other) {
Debug.Log($"进入 {other.gameObject.name} 的触发区域");
}
// 停留在触发区域
void OnTriggerStay(Collider other) {
// 每帧调用
}
// 离开触发区域
void OnTriggerExit(Collider other) {
Debug.Log($"离开 {other.gameObject.name} 的触发区域");
}
三、碰撞检测的6个关键条件
-
双方都有碰撞体组件
-
至少一方有刚体组件(触发器则至少一方有刚体)
-
双方的碰撞体不能都设置为触发器
-
双方的层级在碰撞矩阵中设置为可交互
-
刚体不能设置为睡眠状态
-
碰撞体的"Enabled"属性必须为true
四、高级碰撞处理技术
1. 碰撞信息获取
void OnCollisionEnter(Collision collision) {
// 获取所有接触点
foreach (ContactPoint contact in collision.contacts) {
Debug.DrawRay(contact.point, contact.normal, Color.red, 2f);
}
// 获取相对速度
float impactForce = collision.relativeVelocity.magnitude;
// 获取碰撞体材质
PhysicMaterial material = collision.collider.material;
}
2. 物理材质 (Physic Material)
-
Dynamic Friction:运动时的摩擦力
-
Static Friction:静止时的摩擦力
-
Bounciness:弹性系数
-
Friction Combine:摩擦力的组合方式
-
Bounce Combine:弹性的组合方式
3. 碰撞忽略与过滤
// 方法1:通过层级
Physics.IgnoreLayerCollision(8, 9); // 忽略8层和9层的碰撞
// 方法2:通过碰撞体
Physics.IgnoreCollision(collider1, collider2);
// 方法3:射线检测时使用LayerMask
int layerMask = ~(1 << 10); // 忽略第10层
Physics.Raycast(..., layerMask);
五、2D碰撞检测的特殊性
1. 专用组件
-
Rigidbody2D
-
Collider2D系列(BoxCollider2D, CircleCollider2D等)
2. 检测方法
void OnCollisionEnter2D(Collision2D collision) {
Vector2 normal = collision.contacts[0].normal;
// 2D特有属性
ColliderDistance2D distanceInfo = collision.collider.Distance(GetComponent<Collider2D>());
}
六、性能优化指南
-
碰撞体选择原则:
-
简单形状优先
-
避免使用MeshCollider
-
复杂形状使用多个简单碰撞体组合
-
-
层级碰撞矩阵:
-
在Edit → Project Settings → Physics中设置
-
取消不必要的层间碰撞
-
-
刚体设置技巧:
-
静态物体不要加刚体
-
设置合适的Interpolate模式
-
调整Collision Detection模式:
-
Discrete(默认)
-
Continuous(快速移动物体)
-
Continuous Dynamic(最高精度)
-
-
-
触发器优化:
-
减少OnTriggerStay中的复杂计算
-
使用Physics.autoSyncTransforms控制更新频率
-
七、常见问题解决方案
1. 碰撞不被检测
-
检查清单:
-
碰撞体是否启用
-
刚体是否存在
-
层级设置是否正确
-
是否有一方是触发器但未正确处理
-
2. 物体穿透问题
-
解决方案:
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
-
-
增加质量差
-
减小Fixed Timestep(Edit → Project Settings → Time)
-
3. 性能下降
-
优化方向:
-
减少活跃刚体数量
-
使用简单碰撞体
-
合理设置Sleep Threshold
-
分帧处理碰撞检测
-
八、实战案例:弹球物理系统
public class Ball : MonoBehaviour {
public float bounceFactor = 1.2f;
void OnCollisionEnter(Collision collision) {
// 获取接触点法线
Vector3 normal = collision.contacts[0].normal;
// 计算反射方向
Vector3 incomingVelocity = GetComponent<Rigidbody>().velocity;
Vector3 reflectVelocity = Vector3.Reflect(incomingVelocity, normal);
// 应用反弹力
GetComponent<Rigidbody>().velocity = reflectVelocity * bounceFactor;
// 根据材质调整效果
PhysicMaterial mat = collision.collider.material;
if(mat != null) {
GetComponent<Rigidbody>().velocity *= mat.bounciness;
}
}
}