Unity学习-物体的碰撞检测

1,157 阅读9分钟

一.引言

前面主要在说单个物体的操作,游戏中最主要的就是玩家与玩家之间的交互了,也就存在物体与物体之间的交互,这就是碰撞相关,下面细细说来

二.刚体

产生碰撞有两个条件,一是两个物体都有碰撞器,二是至少有一个物体有刚体,下面就来介绍什么是刚体 刚体参数1.png 刚体参数2.png 整体信息如上图所示

  1. Mass:质量,从物理学的角度来说,质量越大,惯性也就越大,也就不容易改变物体原有的状态,比如一个质量小的物体去碰撞一个质量大的物体,肯定是很难撞动的,反而自己会被弹飞
  2. Drag:空气阻力,空气阻力越大,物体的下降速度越小,0表示没有空气阻力
  3. Angular Drag:扭矩力的阻力大小,顾名思义,该阻力是阻止物体扭转也就是旋转,扭矩力阻力越大,就越难旋转
  4. Use Gravity:是否受重力影响,这个就和牛顿定理有关了,不勾选,肯定就不下坠
  5. Is Kinematic:若勾选此选项,那么物体的移动只能通过Transform组件的修改来发生,可以说物体不受自然力的影响了 效果1_.gif
    效果二_.gif 这里依次展示上面的效果,第5个效果自行尝试,勾选后无法通过碰撞产生位移
  6. Interpolate:插值运算,让物体移动更加平滑
    • Interpolate:根据前一帧的变换来平滑变换
    • Extrapolate:差值运算,根据下一帧的估计变换来平滑变换
      插值运算通常用于帧与帧之间的间隔很大的情况,比如我们可以设置物理帧为1秒,那自然就不顺畅,加上插值运算,如下图所示,左边Interpolate,右边Extrapolate Interpolate_.gif
      如果帧间隔比较大,那么比较推荐使用Interpolate,很明显刚开始时右边那个物体下落不是很平滑
  7. Collision Detection(碰撞检测模式)
    用于防止快速移动的对象穿过其他对象而不检测碰撞
    就比如说下面的子弹,移动速度非常快,直接穿透了物体,物体来不及进行碰撞检测
    子弹移动演示_.gif 刚体参数3.png 其实只需要去记上图的排列组合表即可,就比如说上面的子弹击中玩家,子弹和玩家无论各自使用哪种模式,只要最终得到的模式是连续检测即可
  8. Constraints:约束,对刚体运动的限制
    • Freeze Position:有选择地停止刚体沿世界X、Y、Z轴的移动
    • Freeze Rotation:有选择地停止刚体围绕局部X、Y、Z轴旋转 只要勾选了对应的一个选项,那么该物体就不会在该方向进行移动或者旋转

综上,刚体其实就是一个组件Rigidbody,然后控制上面的参数来让物体有不同物理上的呈现,或者说Rigidbody这个组件让物体有了力的效果

三.碰撞器

碰撞器是产生碰撞的必须条件,而且是双方都必须具备,下面主要介绍碰撞器的种类以及它们共有的参数 碰撞器参数说明1.png 碰撞器参数说明2.png

  1. 碰撞器共有六种,如上图所示,下面介绍一下它们的公共参数
    • Is Trigger:主要用于进行碰撞检测,忽略物体的物理效果刺穿效果_.gif
      如上图的穿透效果,如果箭不勾选该选项,那么当它接触第一个物体时就会带着它一起往前走,这就是碰撞带来的物理效果,Is Trigger中会有一个与之搭配的碰撞检测函数,后面会提到
    • Center:碰撞器原本是和游戏物体完全重合的,如果我们想让具体产生碰撞的范围所有偏移,可以通过此来设置,有的碰撞器(常用的三种碰撞器)还有Edit Collider这个选项,既可以设置偏移,还可以设置有效碰撞区域的大小,也就是设置碰撞器的大小
  2. 关于三种常用的碰撞器,上图中所示是各自独有的参数,其本质就是用于设置各自碰撞器的大小,也就是碰撞范围的大小,下面通过一张图来理解 碰撞器大小演示_.gif
    从图中效果可以看到,绿色范围是碰撞范围,所以两个物体没有0距离接触就直接发生碰撞了,关于三种碰撞器各自的大小参数调节,可自行尝试
  3. 异形物体使用多种碰撞器组合
    简单说就是聚合体的碰撞效果由各个子物体的碰撞器组成,我们通常制作预制体都会外层使用一个空物体来进行聚合管理,而这个空物体是没有碰撞器的,最终产生的碰撞效果是由它的所有子物体产生的,如下图所示 金字塔碰撞盒总和_.gif
  4. 其余三种不常用的碰撞器
    • Mesh Collider:网格碰撞器,追求精细化,可以使用,很耗性能 碰撞器参数说明3.png
      唯一需要注意的就是第一点,带刚体的网格碰撞器必须勾选第一个参数Convex,否则报错
    • Wheel Collider:环状碰撞器,也叫轮子碰撞器,通常用于车子的4个轮子,需要注意的是,车身的质量一定要很大才能压住,否则会被弹飞,正常来说是1500KG 碰撞器参数说明4.png 汽车效果_.gif
    • Terrain Collider:地形碰撞器,后续涉及到绘制地形相关的知识点时再提,Unity提供了工具进行地形的绘制 碰撞器参数说明5.png
  5. 补充说明物理材质,上面遗留了一个物理材质知识点
    • 物理材质的创建以及使用 物理材质的创建_.gif
    • 关于物理材质的具体调参
      物理材质的概念是相互的,相互之间才起作用 物理材质参数说明.png 物理材质效果_.gif 这里清楚动摩擦因素和静摩擦因素以及弹跳能量的吸收效果即可,这个和物理就很关联了

四.碰撞检测和触发器检测函数

生命周期函数图.bmp 先上图,碰撞检测相关也属于脚本生命周期的一部分,所以我们可以重写(按需重写),也是Unity进行反射调用

  1. 碰撞检测
    只要一个物体是刚体(有Rididbody组件),发生碰撞时就会调用如下方法
        //碰撞触发接触时会 自动执行这个函数
        private void OnCollisionEnter(Collision collision)
        {
            //Collision类型的 参数 包含了 碰到自己的对象的相关信息
    
            //关键参数
            //1.碰撞到的对象碰撞器的信息
            //collision.collider
    
            //2.碰撞对象的依附对象(GameObject)
            //collision.gameObject
    
            //3.碰撞对象的依附对象的位置信息
            //collision.transform
    
            //4.触碰点数相关
            //collision.contactCount
            //接触点 具体的坐标
            //ContactPoint[] pos = collision.contacts;
    
            //只要得到了 碰撞到的对象的 任意一个信息 就可以得到它的所有信息
            print(this.name + "被" + collision.gameObject.name + "撞到了");
        }
    
        //碰撞结束分离时  会自动执行的函数
        private void OnCollisionExit(Collision collision)
        {
            print(this.name + "被" + collision.gameObject.name + "结束碰撞了");
        }
    
        //两个物体相互接触摩擦时 会不停的调用该函数
        private void OnCollisionStay(Collision collision)
        {
            print(this.name + "一直在和" + collision.gameObject.name + "接触");
        }
    
    Collision参数保存了很多和当前挂载对象碰撞的物体的信息
  2. 触发器检测函数
    只要当前挂载的物体勾选了Is Trigger选项,从它穿透一个物体的过程中就会执行如下方法
        //触发开始的函数 当第一次接触时 会自动调用
        protected virtual void OnTriggerEnter(Collider other)
        {
            print(this.name + "被" + other.gameObject.name + "触发了");
        }
    
        //触发结束的函数 当水乳相融的状态结束时 会调用一次
        private void OnTriggerExit(Collider other)
        {
            print(this.name + "被" + other.gameObject.name + "结束水乳相融的状态了");
        }
    
        //当两个对象 水乳相融的时候 会不停调用
        private void OnTriggerStay(Collider other)
        {
            print(this.name + "和" + other.gameObject.name + "正在水乳相融");
        }
    

下面整体看下效果,左边的物体是触发检测,右边是碰撞检测 碰撞检测效果_.gif

五.刚体加力

给刚体加力的目的是让其有一个速度朝向某一个方向移动

  1. 获取刚体组件
            Rigidbody rigidBody;
            rigidBody = this.GetComponent<Rigidbody>();
    
  2. 添加力
            //相对世界坐标
            //世界坐标系 Z轴正方向加了一个力
            //加力过后 对象是否停止移动 是由阻力决定的
            //如果阻力为0 那给了一个力过后 始终 是不会停止运动
            //rigidBody.AddForce(Vector3.forward * 10);
            //如果想要在 世界坐标系方法中 让对象 相对于自己的面朝向动
            //rigidBody.AddForce(this.transform.forward * 10);
    
            //相对本地坐标
            //rigidBody.AddRelativeForce(Vector3.forward * 10);
    
    
            //3.添加扭矩力,让其旋转
            //相对世界坐标
            //rigidBody.AddTorque(Vector3.up * 10);
            //相对本地坐标
            //rigidBody.AddRelativeTorque(Vector3.up * 10);
    
            //4.直接改变速度
            //这个速度方向 是相对于 世界坐标系的 
            //如果要直接通过改变速度 来让其移动 一定要注意这一点
            //rigidBody.velocity = Vector3.forward * 5;
    
            //5.模拟爆炸效果
            //模拟爆炸的力 一定是 所有希望产生爆炸效果影响的对象 
            //都需要得到他们的刚体 来执行这个方法 才能都有效果
            //rigidBody.AddExplosionForce(100, Vector3.zero, 10);
    
    理清楚相对世界坐标和相对本地坐标的两种API即可
  3. 力的几种模式
    • 添加不同力的方法
          //第二个参数 力的模式 主要的作用 就是 计算方式不同而已 
          //由于4中计算方式的不同 最终的移动速度就会不同
          //rigidBody.AddForce(Vector3.forward * 10, ForceMode.Acceleration);
      
    • 补充动量定理
          //Ft = mv
          // v = Ft/m;
          //F:力
          //t:时间
          //m:质量
          //v:速度
      
    • Acceleration
          //给物体增加一个持续的加速度,忽略其质量
          //v = Ft/m
          //F:(0,0,10)
          //t:0.02s
          //m:默认为1
          //v = 10*0.02/ 1 = 0.2m/s
          //每物理帧移动0.2m/s*0.02 = 0.004m
      
    • Force
          //给物体添加一个持续的力,与物体的质量有关
          //v = Ft/m
          //F:(0,0,10)
          //t:0.02s
          //m:2kg
          //v = 10*0.02/ 2 = 0.1m/s
          //每物理帧移动0.1m/s*0.02 = 0.002m
      
    • Impulse
          //给物体添加一个瞬间的力,与物体的质量有关,忽略时间 默认为1
          //v = Ft/m
          //F:(0,0,10)
          //t:默认为1
          //m:2kg
          //v = 10*1/ 2 = 5m/s
          //每物理帧移动5m/s*0.02 = 0.1m
      
    • VelocityChange
          //给物体添加一个瞬时速度,忽略质量,忽略时间
          //v = Ft/m
          //F:(0,0,10)
          //t:默认为1
          //m:默认为1
          //v = 10*1/ 1 = 10m/s
          //每物理帧移动10m/s*0.02 = 0.2m
      
    上述四种模式,最贴近现实的就是Force
  4. 力场脚本
    尽管有上述几种力的模式,如果只实现一些简单的功能,使用力场脚本即可,比如让物体不断的旋转,或者运动 力场脚本_.gif 添加力场脚本时,会检测是否添加刚体组件,如果没有会自动添加上
  5. 补充说明刚体的休眠
    Unity为了节约性能,给刚体设置了一个休眠状态,也就是说不是每分每秒都去检测物体当前的受重力状态,如下图 刚体休眠_.gif 从图中可以看到明显的延迟,或者说休眠,可以在Update中解除休眠
        //获取刚体是否处于休眠状态 如果是 
        if (rigidBody.IsSleeping())
        {
            //就唤醒它
            rigidBody.WakeUp();
        }
    

六.总结

本文内容比较多,涵盖了游戏物体碰撞的方方面面的东西,重在理解吧,Unity3D世界很多和物理世界的特性类似