碰撞是游戏开发无法回避的一个话题,当我们的利刃划过敌人的躯体,子弹击中目标的头部,这些都是游戏物体和游戏物体之间的碰撞。那么我们怎么为我们想要的碰撞附加我们想要执行的事件函数呢?下面就为大家揭晓。
一. 碰撞器(Collider)
碰撞器(Collider)是组件,加了碰撞器的游戏物体才可能实现碰撞效果。在Unity内部提供了许多碰撞器,通过Add Component -> Physics可以添加3D碰撞器组件1。Unity提供的组件有:BoxCollider(盒碰撞器), SphereCollider(球碰撞器), CapsuleCollider(胶囊碰撞器), MeshCollider(网格碰撞器), WheelCollider(轮子碰撞器,用来创建交通工具), TerrainCollider(地形碰撞器), CharacterController(角色控制器)。
其中前三种为基元碰撞器,MeshCollider为常用碰撞器(后几种碰撞器有着特殊作用)。其中BoxCollider结构最简单,消耗最小,而MeshCollider最为复杂,消耗很高,且需要提供网格模型。
当两个碰撞体(Collider)发生了碰撞(Collision),且其中至少一个附加了刚体(Rigibody),会向它们附加的对象发射三条碰撞信息,可以在脚本里处理这些信息。
//其中collision为本次碰撞信息集合,从中可以读取与之发生碰撞的碰撞体,碰撞的冲击力等信息
private void OnCollisionEnter(Collision collision) { } //当两个碰撞体刚互相接触时
private void OnCollisionStay(Collision collision) { } //当两个碰撞体持续保持接触状态时
private void OnCollisionExit(Collision collision) { } //当两个碰撞体停止互相接触时
这里要详细介绍一下网格碰撞体(MeshCollider),其采用网格资源并基于该网格构建其碰撞体:
碰撞网格使用背面剔除,如果对象与图形方式背面剔除的网格碰撞,则不会以物理方式与它碰撞(故两个网格碰撞体无法碰撞)。但若是该网格碰撞体标记为凸体(Convex),则可以和其它网格碰撞体发生碰撞,标记为凸体的网格碰撞体网格限制为255个三角形。
二. 触发器(Trigger)
当我们需要检测游戏角色进入到某个房间里,这个时候你可能就会需要用到触发器了。触发器和碰撞器类似,碰撞器只需要将勾选上IsTrigger属性框就成了触发器。触发器发生碰撞时,同样会向它们附加的对象发射三条触发信息,不同的是其并不会发生物理碰撞效果。通过以下函数处理触发信息:
//其中collider为发生碰撞的碰撞体
private void OnTriggerEnter(Collider collider) { } //当碰撞体进入到触发器时
private void OnTriggerStay(Collider collider) { } //当碰撞体和触发器有重叠区域时
private void OnTriggerExit(Collider collider) { } //当碰撞体离开触发器时
三. 刚体
刚体(Rigibody),为游戏对象赋予物理属性,使游戏对象实现现实世界中的运动效果的组件。刚体2是物理引擎中最基本的组件,通过刚体组件,可以为游戏物体添加一些常见的物理属性,如质量,摩擦力,碰撞参数等。
刚体的插值(Interpolate):物体运动插值模式,当发现刚体运动时抖动,可以尝试变换下面选项:
- None: 无,不应用插值;
- Interpolate: 内插值,基于上一帧Transform来平滑本帧Transform;
- Extrapolate: 外插值,基于预测的下一帧Transform来平滑本帧Transform;
刚体的三种碰撞检测模式(CollisionDetection):
- Discrete: 离散检测模式,普通默认状态,不适用于高速运动的物体,会有穿透的问题;尽量使用这一种;
- Continuous: 连续检测,更加精细的碰撞检测,但是比较消耗性能;在这种状态下,此刚体与其它普通刚体碰撞时,仍使用Discrete的碰撞检测,与没有刚体的MeshCollider碰撞就会转为连续检测,防止穿过所有静态碰撞体;
- Continuous Dynamic: 动态连续检测,对没有刚体的MeshCollider,和处在Continuous或ContinuousDynamic状态下的刚体使用连续碰撞检测,对其它刚体使用Discrete检测,防止对象穿过所有静态碰撞体及设置为Continuous或ContinuousDynamic的刚体。
四. 发生碰撞触发的条件
不是随随便便两个物体附加了碰撞体都可以发生碰撞,无论是发生碰撞还是触发都有一定的条件的。一般发生碰撞(触发),至少一方身上挂有刚体(Rigibody)。在了解发生条件之前,我们先要了解这几种碰撞体状态:
- Static Collider 静态碰撞器
静态碰撞器是指一个包含碰撞器但不含刚体的游戏对象,这种游戏对象应当保持静止或很少移动。因此十分适用于环境几何结构,它们在与刚体发生碰撞时不会移动; - Rigidbody Collider 刚体碰撞体
是指同时包含了刚体和碰撞体的游戏物体,受物理引擎影响。 - Kinematic Rigidbody Collider 运动学刚体碰撞体
此游戏对象同样包含有碰撞体和刚体,不同的是,它的刚体是运动学的(不受物理影响,只能直接设置)。适用于要经常四处移动的静态碰撞体,来发送触发器(Trigger)事件的情况。能够用来唤醒其它休眠的刚体。
则几种碰撞体相互之间碰撞触发的条件为:
触发器的触发发生条件:
附录:物理设置
默认情况下,碰撞体会和所有满足条件的碰撞体发生碰撞。但有些时候,我们希望某个碰撞体只和特定的碰撞体发生碰撞,这可以通过在Edit->ProjectSettings->Physics中设置。各个版本的Unity该选项的内容可能不同,建议根据Unity用户手册->Working in Unity->Editor Features->Project Settings->Physics中详细了解各个属性的作用。
这里以Unity2017的Physics为例:
其中的各个属性为:
- Gravity: 设定全局重力大小,需要注意的是,如果你增大了默认重力,最好同时增加DefaultSolverIterations值来维持稳定接触;
- Default Material: 设置碰撞体的默认物理材质,当碰撞体没有分配物理材质时,将为其分配默认材质;
- Bounce Threshold: 设置一个速度值,当两个碰撞体的相对速率小于这个值时,它们不会再弹开。这个值也用来减少抖动,因此不推荐设置的很小;
- Sleep Threshold: 设置一个全局动能值,非运动学刚体动能小于这个值的将会进入休眠状态(不再每帧更新)以减小性能消耗;
- Default Contact Offset: 设定碰撞检测系统判定发生碰撞时两个碰撞体间的最大的距离,这个值必须为正,且如果太过于接近零将造成抖动;
- Default Solver Iterations: 定义在每个物理帧有多少个求解器(小型物理引擎,决定物理间的相互作用)运行。这将影响求解器输出的质量(当物理更新改变或有额外的需求时建议更改值),它可以减少由关节或接触产生的抖动;
- Default Solver Velocity Iterations: 定义在每个物理帧有多少个求解器执行速度进程。这将影响刚体(碰撞后)反弹的准确性。当你需要获得具有多个刚体的游戏物体碰撞后的准确表现结果,尝试增加这个值;
- Queries Hit Backfaces: 若被勾选,则允许对碰撞体的网格三角形执行背面检测(如从一个碰撞体内部发出的射线可以检测到该碰撞体);
- Queries Hit Triggers: 若被勾选,则意味着物理检测(如射线)可以检测到触发器;
- Enable Adaptive Force: 若被勾选,则采用适应力。当一个力作用于一整堆物体(如一整堆箱子)时,适应力能提供一个更真实的表现。
- Enable PCM: 若被勾选,则启用Persistent Contacts Manifold方式生成碰撞;
- Auto Simulation: 若被勾选,则自动运行物理模拟或允许对其直接控制;
- Layer Collision Matrix: 层碰撞矩阵,其中的Water, UI等层就是在Edit->Project Settings->Tags And Layers中Layers里设定的层。打勾表示该行该列对应的两个层上的碰撞体可以发生碰撞,没有勾则表示对应的两个层上的碰撞体不能发生碰撞;