一.引言
前面主要在说单个物体的操作,游戏中最主要的就是玩家与玩家之间的交互了,也就存在物体与物体之间的交互,这就是碰撞相关,下面细细说来
二.刚体
产生碰撞有两个条件,一是两个物体都有碰撞器,二是至少有一个物体有刚体,下面就来介绍什么是刚体
![]()
整体信息如上图所示
- Mass:质量,从物理学的角度来说,质量越大,惯性也就越大,也就不容易改变物体原有的状态,比如一个质量小的物体去碰撞一个质量大的物体,肯定是很难撞动的,反而自己会被弹飞
- Drag:空气阻力,空气阻力越大,物体的下降速度越小,0表示没有空气阻力
- Angular Drag:扭矩力的阻力大小,顾名思义,该阻力是阻止物体扭转也就是旋转,扭矩力阻力越大,就越难旋转
- Use Gravity:是否受重力影响,这个就和牛顿定理有关了,不勾选,肯定就不下坠
- Is Kinematic:若勾选此选项,那么物体的移动只能通过Transform组件的修改来发生,可以说物体不受自然力的影响了
这里依次展示上面的效果,第5个效果自行尝试,勾选后无法通过碰撞产生位移
- Interpolate:插值运算,让物体移动更加平滑
- Interpolate:根据前一帧的变换来平滑变换
- Extrapolate:差值运算,根据下一帧的估计变换来平滑变换
插值运算通常用于帧与帧之间的间隔很大的情况,比如我们可以设置物理帧为1秒,那自然就不顺畅,加上插值运算,如下图所示,左边Interpolate,右边Extrapolate
如果帧间隔比较大,那么比较推荐使用Interpolate,很明显刚开始时右边那个物体下落不是很平滑
- Collision Detection(碰撞检测模式)
用于防止快速移动的对象穿过其他对象而不检测碰撞
就比如说下面的子弹,移动速度非常快,直接穿透了物体,物体来不及进行碰撞检测其实只需要去记上图的排列组合表即可,就比如说上面的子弹击中玩家,子弹和玩家无论各自使用哪种模式,只要最终得到的模式是连续检测即可
- Constraints:约束,对刚体运动的限制
- Freeze Position:有选择地停止刚体沿世界X、Y、Z轴的移动
- Freeze Rotation:有选择地停止刚体围绕局部X、Y、Z轴旋转 只要勾选了对应的一个选项,那么该物体就不会在该方向进行移动或者旋转
综上,刚体其实就是一个组件Rigidbody,然后控制上面的参数来让物体有不同物理上的呈现,或者说Rigidbody这个组件让物体有了力的效果
三.碰撞器
碰撞器是产生碰撞的必须条件,而且是双方都必须具备,下面主要介绍碰撞器的种类以及它们共有的参数
![]()
- 碰撞器共有六种,如上图所示,下面介绍一下它们的公共参数
- Is Trigger:主要用于进行碰撞检测,忽略物体的物理效果
如上图的穿透效果,如果箭不勾选该选项,那么当它接触第一个物体时就会带着它一起往前走,这就是碰撞带来的物理效果,Is Trigger中会有一个与之搭配的碰撞检测函数,后面会提到 - Center:碰撞器原本是和游戏物体完全重合的,如果我们想让具体产生碰撞的范围所有偏移,可以通过此来设置,有的碰撞器(常用的三种碰撞器)还有
Edit Collider这个选项,既可以设置偏移,还可以设置有效碰撞区域的大小,也就是设置碰撞器的大小
- Is Trigger:主要用于进行碰撞检测,忽略物体的物理效果
- 关于三种常用的碰撞器,上图中所示是各自独有的参数,其本质就是用于设置各自碰撞器的大小,也就是碰撞范围的大小,下面通过一张图来理解
从图中效果可以看到,绿色范围是碰撞范围,所以两个物体没有0距离接触就直接发生碰撞了,关于三种碰撞器各自的大小参数调节,可自行尝试 - 异形物体使用多种碰撞器组合
简单说就是聚合体的碰撞效果由各个子物体的碰撞器组成,我们通常制作预制体都会外层使用一个空物体来进行聚合管理,而这个空物体是没有碰撞器的,最终产生的碰撞效果是由它的所有子物体产生的,如下图所示 - 其余三种不常用的碰撞器
- Mesh Collider:网格碰撞器,追求精细化,可以使用,很耗性能
唯一需要注意的就是第一点,带刚体的网格碰撞器必须勾选第一个参数Convex,否则报错 - Wheel Collider:环状碰撞器,也叫轮子碰撞器,通常用于车子的4个轮子,需要注意的是,车身的质量一定要很大才能压住,否则会被弹飞,正常来说是1500KG
- Terrain Collider:地形碰撞器,后续涉及到绘制地形相关的知识点时再提,Unity提供了工具进行地形的绘制
- Mesh Collider:网格碰撞器,追求精细化,可以使用,很耗性能
- 补充说明物理材质,上面遗留了一个物理材质知识点
- 物理材质的创建以及使用
- 关于物理材质的具体调参
物理材质的概念是相互的,相互之间才起作用这里清楚动摩擦因素和静摩擦因素以及弹跳能量的吸收效果即可,这个和物理就很关联了
- 物理材质的创建以及使用
四.碰撞检测和触发器检测函数
先上图,碰撞检测相关也属于脚本生命周期的一部分,所以我们可以重写(按需重写),也是Unity进行反射调用
- 碰撞检测
只要一个物体是刚体(有Rididbody组件),发生碰撞时就会调用如下方法Collision参数保存了很多和当前挂载对象碰撞的物体的信息//碰撞触发接触时会 自动执行这个函数 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 + "接触"); } - 触发器检测函数
只要当前挂载的物体勾选了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 + "正在水乳相融"); }
下面整体看下效果,左边的物体是触发检测,右边是碰撞检测
五.刚体加力
给刚体加力的目的是让其有一个速度朝向某一个方向移动
- 获取刚体组件
Rigidbody rigidBody; rigidBody = this.GetComponent<Rigidbody>(); - 添加力
理清楚相对世界坐标和相对本地坐标的两种API即可//相对世界坐标 //世界坐标系 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); - 力的几种模式
- 添加不同力的方法
//第二个参数 力的模式 主要的作用 就是 计算方式不同而已 //由于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
- 添加不同力的方法
- 力场脚本
尽管有上述几种力的模式,如果只实现一些简单的功能,使用力场脚本即可,比如让物体不断的旋转,或者运动添加力场脚本时,会检测是否添加刚体组件,如果没有会自动添加上
- 补充说明刚体的休眠
Unity为了节约性能,给刚体设置了一个休眠状态,也就是说不是每分每秒都去检测物体当前的受重力状态,如下图从图中可以看到明显的延迟,或者说休眠,可以在Update中解除休眠
//获取刚体是否处于休眠状态 如果是 if (rigidBody.IsSleeping()) { //就唤醒它 rigidBody.WakeUp(); }
六.总结
本文内容比较多,涵盖了游戏物体碰撞的方方面面的东西,重在理解吧,Unity3D世界很多和物理世界的特性类似