连续碰撞检测
高速运动的物体需要连续碰撞检测解决离散帧在碰撞检测失败的问题,如子弹。连续碰撞检测(Continuous Collision Detection)简称CCD。
CCD有几种解决方案:
1、Sub-Stepping
子步,又称Supersampling超采样,是UE4的物理引擎PhysxCCD的一种实现方法(不确定是否是默认的实现方法,见https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/AdvancedCollisionDetection.html),打开UE4的CCD默认开关会启动Physx的默认Flag。
(如图,UE4的UseCCD是直接使用PhysX的标志位启动默认CCD)
同时,UE4也提供ProjectileMovementComponent的Sub-Stepping和全局的Sub-Stepping,全局Sub-Stepping见(https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Physics/Substepping/)。
一般来说Sub-Stepping不会全程执行,而是在粗检阶段进行一定的预测,检测到潜在的碰撞帧与碰撞时间,才开始子步的计算。
Unity的Sweep-based CCD也是基于Sub-Stepping,并在执行Sub-Stepping之前进行线性预测来提升性能,见(https://docs.unity3d.com/2020.3/Documentation/Manual/ContinuousCollisionDetection.html的Sweep-based CCD部分)。
毫无疑问子步会增加物理更新的性能消耗,但能轻松解决子弹问题和抖动问题,取决于项目能拿出多少帧时间来更新子步。
2、二分法
二分法用于搜索物体有碰撞帧来反推碰撞时间点。假设第1帧没有碰撞,第2帧发生了碰撞,并且时间是0-1,从0.5开始检测,如果没有发生碰撞,则从0.75继续检测,否则从0.625开始检测。
这种做法能快速查到一定精度的碰撞时间点,但是无法检测到两帧都没有碰撞的穿透情况。这种方法一般做预测,并结合SubStepping使用。
3、基于Raycast的无弹道碰撞
射击游戏的子弹是高速运动的,很多时候到达目标也就一两帧的事情(尤其是狙击枪)。直接使用射线检测到目标点能准确地检测到射击的碰撞点。如果是大型物体,使用物体的重心,或者给物体划分几个重点碰撞点能稍准确地获取到碰撞点。
目前项目没有用到这方法,现在计划是若有此需求出现再实现。
4、球体Trace
可以使用球体Trace的近似模拟来解决CCD问题。
SphereTrace使用前一帧为初始点,当前帧为目标点,以策划配置的DamageTrace的Radius为半径进行Trace碰撞查询。
SphereTrace能避免任何漏掉的碰撞,并能检测到准确的碰撞位置,但是不能使用精确的碰撞体(如子弹是个锥体)。使用目前这种做法能满足大部分需求。
5、Speculative CCD
基于预测的子步,球体Trace都是线性的检测方法,在带有角速度的物体有时候会检测失效。Physx提供一种称为Speculative CCD的检测方法,通过膨胀物体的碰撞体(如膨胀成AABB),获取所有潜在的碰撞接触点,并通过求解器计算筛选最合适的触点。
该算法性能优于Sweep-Based CCD/SubStepping,并且能用在角速度的物体上。
但是Speculative CCD在某些情况会导致幽灵碰撞(在曲面细分的平面上平移却突然往上跳),以及穿透(当在碰撞时获得反馈能量过大)。
参考Unity的Speculative CCD(https://docs.unity3d.com/2020.3/Documentation/Manual/ContinuousCollisionDetection.html)
也可参考Physx的Speculative CCD部分源码(文档没有细讲,https://github.com/NVIDIAGameWorks/PhysX)
6、凸面体碰撞
两凸面体的高速碰撞,若球体Trace的精度还无法满足,可参考(http://www.dtecta.com/papers/unpublished04raycast.pdf),或者书籍(https://book.douban.com/subject/2586364/)。
7、带角速度高速碰撞
除了Speculative CCD和使用非线性预测的SubStepping,以上线性预测的CCD算法都是无法处理角速度高速运动物体的碰撞。
一种保守式步进的迭代的方式来处理旋转移动:(http://graphics.ewha.ac.kr/fast/)