Unity物理碰撞检测详解

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个关键条件

  1. 双方都有碰撞体组件

  2. 至少一方有刚体组件(触发器则至少一方有刚体)

  3. 双方的碰撞体不能都设置为触发器

  4. 双方的层级在碰撞矩阵中设置为可交互

  5. 刚体不能设置为睡眠状态

  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>());
}

六、性能优化指南

  1. 碰撞体选择原则

    • 简单形状优先

    • 避免使用MeshCollider

    • 复杂形状使用多个简单碰撞体组合

  2. 层级碰撞矩阵

    • 在Edit → Project Settings → Physics中设置

    • 取消不必要的层间碰撞

  3. 刚体设置技巧

    • 静态物体不要加刚体

    • 设置合适的Interpolate模式

    • 调整Collision Detection模式:

      • Discrete(默认)

      • Continuous(快速移动物体)

      • Continuous Dynamic(最高精度)

  4. 触发器优化

    • 减少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;
        }
    }
}

### Unity 中的碰撞检测实现方法 在 Unity 的开发过程中,碰撞检测是一个非常重要的功能模块。以下是几种常见的碰撞检测方式及其具体实现: #### 射线检测 射线检测是一种常用的碰撞检测技术,在许多场景下用于判断物体之间的交互关系。通过 `Physics.Raycast` 函数可以轻松实现这一目标[^1]。 ```csharp using UnityEngine; public class RaycastExample : MonoBehaviour { void Update() { Vector3 origin = transform.position; Vector3 direction = transform.forward; if (Physics.Raycast(origin, direction, out RaycastHit hit)) { Debug.Log($"Hit object: {hit.collider.name}"); } } } ``` 上述代码展示了如何使用射线投射来检测前方是否有对象被命中,并打印出该对象的名字。 --- #### 静态方法调用 Input 类 Unity 提供了许多内置工具帮助开发者快速完成输入处理工作。例如,`Input.GetAxis` 是一种获取用户输入的方式之一。需要注意的是,这些函数均为静态方法,因此可以直接通过类名访问而无需创建实例[^2]。 如果尝试在一个非静态上下文中操作,则可能会遇到错误提示:“无法从静态字段或属性中引用非静态成员”。这表明当前环境不允许此类行为发生。 --- #### 扇形范围内的点判定 对于某些特殊需求来说,可能需要知道某一点是否位于特定角度范围内(比如技能释放区域)。此时可以通过计算两个向量间夹角的方法来进行验证[^3]。 假设我们已经得到了玩家位置至目标位置的方向矢量以及技能发射方向矢量,则可通过如下逻辑得出结论: ```csharp float angleThreshold = Mathf.Deg2Rad * maxAngle / 2f; // Convert degree to radian and half it. Vector3 normalizedA = a.normalized; Vector3 normalizedB = b.normalized; if(Vector3.Dot(normalizedA, normalizedB) >= Math.Cos(angleThreshold)){ // Inside the cone area. } else { // Outside the cone area. } ``` 这里的关键在于理解三角学中的余弦定理应用——当两单位长度向量做内积运算结果大于等于给定阈值时即满足条件。 --- ### 总结 以上介绍了三种不同类型的碰撞或者空间关系查询手段:基于物理引擎的传统射线测试;针对键盘鼠标事件捕捉机制下的简单指令执行模式切换;还有就是几何图形算法方面的基础运用案例分析。每种都有其适用场合,合理选用才能达到最佳效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值