碰撞检测问题及解决
在Unity3D中,碰撞可以形象理解为现实世界中的碰撞,并且由于物理引擎的模拟,使碰撞物体也会如现实世界一样产生相同的碰撞结果,如运动轨迹的改变、反弹等,总之碰撞的结果为产生我们不期望的物体运动行为。为了避免产生这种碰撞,就需要进行碰撞检测。
碰撞检测实现一
碰撞检测的原理是在物体移动的前方放置一个虚拟物体,它与物体进行同步运动。如果有障碍,则要先与虚拟物体碰撞。一旦检测到虚拟物体的碰撞,则运动物体停止移动。
- 实现时则是使用虚拟射线来进行检测,检测时使用到的api分别如下截图:
- 碰撞检测的具体实现如下截图:
- 碰撞检测的调用实现如下截图:
图 - 3 调用碰撞的实现
实现一的问题及分析
以上实现经过分析,原理想法上没问题,就是每隔一个固定频率后进行射线碰撞检测。但是效果上却总是会在一些偶然的情况下发生碰撞,具体情形如下截图:
图 - 4 碰撞导致不期望的运动
- Update受制于FPS问题
经过分析,发现update虽然是每帧执行的,但是Unity3D的FPS——每秒的帧数,是变化的,它的值取决于渲染场景的面数;渲染速度又受机器的性能特别是CPU、内存、显卡不同而表现很大的差异。如果场景中渲染的面数太多,并且机器配置不够好,可能导致FPS很低,从而使渲染的两帧之间时间间隔很大,而在这个间隔内,碰撞已经发生,从而出现了上面的碰撞现象。
对于物理运动中需要进行固定频率的动作,Unity3D提供了FixedUpdate方法,这个频率是系统参数,可以更改。因此,我们使用FixedUpdate来替换Update可解决此问题。
- 射线检测问题
如图 - 2 碰撞检测实现中,虽然多加了几条射线,但毕竟只是物体中的个别点。在我们的场景中,物体都是规则的且正常运动规律显著,所以只需要把几个特征点找到然后分别进行射线检测即可。但是这个毕竟是存在缺陷的,如果能够对物体整个面进行检测,那就完美了。
幸运的是,自Unity3D 版本3以来,针对刚体的碰撞检测问题,专门提供了Rigidbody.SweepTest方法。需要注意的是Physics.Raycast里面有一个层的概念,而且不是必须刚体才会被检测到;而Rigidbody.SweepTest则只检测刚体碰撞,与层无关。
碰撞检测的改善性实现
经过以上分析,可以知道碰撞检测会用到关键性方法Rigidbody.SweepTest,它的API如下截图:
图 - 5 刚体碰撞检测的API
改善后的实现如下截图:
图 - 6 改善性实现代码
经过试验,改善后的实现可以很好解决碰撞问题