Unity3d 开发技术

碰撞器

2018-06-06  本文已影响11人  波波课堂

相关文章

《物理系统概述》
《刚体》
《碰撞器》

概述

刚体的主要作用是使物体能够受力并施力,但只有刚体组件的物体不能进行碰撞检测,我们需要额外为物体定义它的物理边界,才可以进行碰撞。碰撞器Collider组件就是划定物理边界的组件。一般情况下我们场景中会有三种物体。

两个物体,如果它们身上只有刚体组件,运动时是不会发生相互碰撞的,而是互相穿透对方。除非它们两个都具有碰撞器,碰撞检测才能正常发生。碰撞发生的必要条件就是:

两个物体中至少一个带有刚体,并且两个物体都应具有碰撞器

没有碰撞器肯定不会发生碰撞。

图 3-1 碰撞发生条件

碰撞器不是一个组件,而是多个组件的统称,不同的碰撞器划定的物理边界形状不同。Unity 为我们提供了 3 个简单的基本碰撞器组件:盒型碰撞器Box Collider球形碰撞器Sphere Collider胶囊碰撞器Capsule Collider

图 3-2 三种基本碰撞器

因为基本碰撞器的形状简单、面数少,在碰撞检测时速度更快,节省物理资源,所以平时尽可能地使用基本碰撞器。如果要进行碰撞检测的模型形状比较复杂,使用基本碰撞器无法很好地模拟物体形状时,有两种解决方法:可以使用多个基本碰撞器进行组合,近似地模拟物体形状,或者使用网格碰撞器Mesh Collider

图 3-3 多个基本碰撞器进行组合

网格碰撞器能够精确地匹配游戏对象的网格形状。但因为它的面数多,所以尽可能不要使用它。

图 3-4 游戏模型和它的网格碰撞器

除此以外,还有三个特殊功能的碰撞器。车轮碰撞器Wheel Collider专门模拟汽车轮胎的物理行为,用来创建赛车竞速类游戏项目。地形碰撞器Terrain Collider用于 Unity 内置的地形系统进行碰撞检测(Terrain 地形系统可以快速创建场景地形,但真正项目中几乎不用),还有一个用于微软 HoloLens 混合现实开发的碰撞器,叫做空间映射碰撞器Spatial mapping Collider。不过这些碰撞器都不常用到,除非我们要做的项目是赛车类或者 HoloLens 开发也许会使用它们。

基本碰撞器

接下来我们要分别了解盒型碰撞器、球形碰撞器、胶囊碰撞器,它们都是组件,其实也是脚本,或者说是类。所有的碰撞器组件都有一个共同的父类Collider,在父类中定义了碰撞器共同的行为特征,而子类的主要功能只是根据各自的不同形状,定义物理边界的大小。我们可以先看看这些不同碰撞器中的相同属性,他们都是在父类中定义的。

图 3-5 基本碰撞器组件

在盒型碰撞器、球形碰撞器和胶囊碰撞器组件中,都有触发器Is Trigger属性和物理材质Material属性,这两个属性都是在它们共同的父类 Collider 中定义的。触发器属性勾选后,碰撞器会成为一个可穿过的特殊区域,叫做触发器,当有刚体穿过该区域时脚本中会收到事件,用来实现一些特殊效果,比如人物进入某个区域范围时会触发剧情。物理材质属性可以定义碰撞器的弹力、摩擦力等物理细节,能够制作出乒乓球(弹力大)、冰面(光滑,摩擦力小)等特殊的物理效果。

在父类中还提供了一个属性attachedRigidbody,表示碰撞器所附属的刚体,在属性面板中看不到这个属性,在脚本中可以使用。

附属的刚体Rigidbody attachedRigidbody { get; }

这是一个 Rigidbody 类型的属性,只有 Get 方法,会返回当前 Collider 游戏对象上的刚体,如果 Collider 游戏对象上没有刚体,会继续往父对象身上查找刚体,如果最终都没有刚体,会返回 null。

在碰撞器属性面板中其余的属性,比如中心点、大小、半径、高度等,都是用来调整它们各自的形状大小和位置的。

图 3-6 基本碰撞器形状大小和位置属性

在盒型碰撞器中有中心点Center大小Size两个属性,分别表示盒型碰撞器的几何中心的位置以及长宽高的大小。在球形碰撞器中有中心点半径Radius两个属性,分别表示球形碰撞器的几何中心位置以及球体半径的大小。要注意的是,球形碰撞器永远是一个球形,就算修改缩放(Scale)属性也不可能把它变成一个椭圆形。

图 3-7 球形碰撞器修改缩放

属性面板上这些属性同样可以在脚本中进行获取或修改,比如盒型碰撞器的中心和大小。

球形碰撞器的中心点和半径。

胶囊碰撞器的形状比较特别,看起来就像是一个胶囊。它的两端各是一个半球体,中间是一个圆柱体。

图 3-8 胶囊体

胶囊碰撞器中也有中心点半径这两个属性,除此以外还有高度Height属性,表示从胶囊的一端到另一端的距离,以及一个方向Direction属性,表示胶囊体是沿 XYZ 的哪个轴放置的,它有三个值:X-AxisY-AxisZ-Axis

胶囊碰撞器的方向Direction属性比较特殊,它不是一个枚举类型,而是一个 int 类型的属性。值只能是 0、1 或者 2,分别表示胶囊体的方向是在 X、Y、Z 轴方向上。

图 3-9 胶囊碰撞器方向

网格碰撞器

网格碰撞器不属于基本碰撞器,因为它的形状是由我们提供的网格决定的。给网格碰撞器设置了网格Mesh属性后,碰撞器的物理边界形状就会和网格形状一致。我们可以给场景中的静态物体做一个近似的网格,然后使用网格碰撞器进行碰撞检测。

图 3-10 网格碰撞器组件

在网格碰撞器中,也有触发器物理材质这两个属性。不同的是,网格碰撞器的形状大小和位置不可以手动调整,而是要一个网格资源的引用。

图 3-11 网格碰撞器场景效果

比如场景中有一叠轮胎,角色移动时不可以穿透轮胎,会被碰撞,我们可以在建模软件中单独做一个用于碰撞检测的网格,配合网格碰撞器实现碰撞检测。一般只有场景中完全不动的物体我们才使用网格碰撞器。也就是说:

网格碰撞器一般不会和刚体一同使用,它只会静止在场景中,等待其它刚体和它发生碰撞

网格碰撞器的网格Mesh属性也可以在脚本中获取或修改。而且用于网格碰撞器的网格,最好是经过简化的特制碰撞网格,不要直接使用模型网格,因为模型网格面数过多,会极大地影响性能。

网格碰撞器中使用的网格引用是普通的网格文件,但必须转换成适合物理引擎的网格,这个过程叫做Mesh Cooking,由系统自动完成。在网格碰撞器中有一个 Cooking Options 属性,可以在转换网格时启用一些功能选项。所有的网格在使用前都要经过这个处理过程。一共有四个可选项。

图 3-12 网格碰撞器 Cooking Options 属性

在这个属性中还有一个 NoneEverything 两个选项,分别表示全不选和全选。另外,如果网格碰撞器中使用的网格仅用于物理系统,不用于模型显示的话,可以在模型的导入设置中不导入法线,因为物理系统的网格不需要法线。

图 3-13 法线导入设置

碰撞和碰撞检测

有了刚体和碰撞器,就可以发生碰撞,并且当碰撞发生时,可以在脚本中处理碰撞事件。两个物体能够发生碰撞而不是互相穿透对方,必要条件是至少有一个是刚体,并且两个物体都有碰撞器。设置好属性后,碰撞是自动发生的。

只要游戏对象身上带有刚体或者碰撞器组件,就可以挂载脚本响应碰撞事件。碰撞事件一共有三个,分别表示碰撞开始OnCollisionEnter持续碰撞OnCollisionStay碰撞结束OnCollisionExit

有了这三个事件,我们可以当碰撞持续时在发生碰撞的位置添加一些粒子特效、在碰撞开始时播放一些音效,或者在碰撞结束时销毁某个游戏对象,具体如何使用由我们的功能要求决定。

这三个事件方法都没有返回值,同时都有一个 Collision 类型的参数 other。这个参数中保存了碰撞相关的一些信息。

最常用的就是通过 Collision 类型的参数 other,获取与当前游戏对象发生碰撞的游戏对象、刚体、碰撞器或者 Transform。

我们还能获取两个碰撞对象的相对速度以及冲量。

也可以获取碰撞发生时碰撞点的信息,因为碰撞点有可能不止一个,所以这些点存放在数组中。每个碰撞点我们都能够获取它的世界坐标位置和法线向量。

大部分碰撞都可以正常处理,但也会有一些特殊情况,导致无法正常发生碰撞。最常见的碰撞无效就是因刚体速度过快而穿透碰撞器。因为物理系统对碰撞检测也是每帧检测一次,计算刚体位置更新后是否会和其它碰撞器有接触,这种检测叫做离散碰撞检测。

但如果刚体运动速度过快,有可能发生上一帧还在碰撞器的一侧,但下一帧就运动到了另一侧的情况,此时因为刚体并未接触到障碍物的碰撞器,所以检测不出碰撞的发生,会直接穿透障碍物。

图 3-14 快速运动的刚体穿透障碍物

此时我们可以调整刚体组件上的碰撞检测 Collision Detection 属性,使碰撞能够正常发生。此属性默认是 Discrete 离散检测。

图 3-15 刚体的碰撞检测模式

简单来说,如果刚体运动速度很快导致碰撞没有正常发生,此时障碍物身上如果没有刚体,就使用连续检测,如果障碍物身上也有刚体,就把这两个刚体的碰撞检测模式都设置为连续动态检测。

对于网格碰撞器的碰撞来说,也有一种特殊情况。网格碰撞器一般不推荐移动它的位置,所以一般也不会和刚体一同使用。如果给网格碰撞器添加了刚体,必须勾选Convex 属性。否则它不能和其它碰撞器发生碰撞。

图 3-16 网格碰撞器的凸属性

触发器

触发器是碰撞器的一种特殊状态,每个碰撞器组件中都有一个触发器 Is Trigger 属性,勾选之后这个碰撞器就成为一个触发器。正常的碰撞器发生碰撞时,会弹开对方,不会互相穿透,而触发器不会弹开对方。

触发器划定的物理边界是一个触发范围,其他刚体能够进入触发范围,当有其它刚体进入这个范围时,会在脚本中收到触发相关的事件,进行一些额外处理。比如,如果人物靠近自动门,门就会自己打开。此时可以在门前放一个触发器,当检测到人物进入这个触发范围时,使门播放动画和音效打开。

图 3-17 触发器属性

触发器本身不会阻挡刚体的运动,也不会表现出任何效果,所以使用触发器时需要在脚本中进行额外处理。响应触发事件的脚本可以挂载在刚体身上,也可以挂载在触发器身上。触发事件和碰撞事件很相似,也有三个:触发开始 OnTriggerEnter持续触发 OnTriggerStay触发结束 OnTriggerExit

这三个事件方法同样没有返回值,但都有一个 Collider 类型的参数 other。这个参数表示是哪个碰撞器进入了触发区域,我们可以用这个参数获取到进入出发区域的游戏对象或者刚体,再进一步处理。

物理材质

每一个碰撞器组件中都还有一个物理材质 Material 属性。物理材质是 Unity 内置的一种资源类型,所以需要在 Unity 的 Project 视图中创建一个物理材质。

在 Project 视图中空白区域单击鼠标右键 -> Create -> Physic Material 就可以创建出一个物理材质,他会直接显示在 Project 视图中。

图 3-18 物理材质

之后就能够用它给碰撞器组件中的物理材质(Material)属性赋值。物理材质可以让碰撞器模拟出弹力和摩擦力的效果,选中物理材质资源后,在属性面板中就能看到可以调整的属性信息。

图 3-19 物理材质属性

动摩擦力 Dynamic Friction 表示当其它碰撞器在自己表面滑动时受到的摩擦力大小。它是一个 Float 类型属性,取值范围 0-1。0 表示没有动摩擦力,可以模拟非常光滑的表面,比如冰面。1 表示动摩擦力非常大,用来模拟非常粗糙的表面,其它碰撞器在它表面滑动时很快就会减速静止。

静摩擦力 Static Friction 表示当其它碰撞器静止在自己表面,但有运动倾向时,受到的摩擦力大小。它也是个 Float 类型属性,取值范围 0-1。

弹力 Bounciness 表示当前碰撞器表面的弹性,Float 类型,取值范围 0-1。0 表示不会反弹,1 表示反弹时没有能量损失。

摩擦力组合 Friction Combine弹力组合 Bounce Combine。因为摩擦力和弹力效果都需要两个碰撞器相互作用,如果另一个碰撞器也设置了摩擦力和弹力,此时应如何计算最终的结果。有四种计算方式。

当然,这些属性也可以在脚本中获取或进行修改。

但物理系统的模拟并不能完全和现实中的物理效果一样,比如弹力为 1 的小球自由落体撞击弹力为 1 的地面,此时小球反弹也许会达到比初始位置更高的高度。所以我们在使用时要根据预期效果调整这些物理参数。

(The End)

上一篇 下一篇

猜你喜欢

热点阅读