总是碰到关于碰撞的问题,今天实在忍不住了,来把它搞懂,不然听到八叉树,BSP什么的就怕可不行。 

碰撞机制

最近做动态地形生成的时候,发现碰撞检测无效,于是查阅了相关资料,大体上把unity的检测流程弄清楚了

碰撞检测,就是检测两个物体是否相交,如果物体非常规则,比如球体,直接检测圆心距离是否小于半径和即可,计算量十分小,但是,如果物体不规则,比如一个角色,进行十分细致的碰撞检测就会变的十分困难,这时候,我们一般会用简单几何体去逼近复杂网格

unity碰撞检测 检测不到 unity碰撞检测原理_八叉树


如上图所示,我用4个圆去逼近一个多边形,注意,下层圆圆心位于根圆到顶点连线上,且圆心位于上层圆边上的,类比到3d空间也是如此,可以用球体去趋近网格,这里需要注意,凹多边形的逼近是难计算的,所以内部会将其拆分成多个凸多边形

unity碰撞检测 检测不到 unity碰撞检测原理_unity_02


现在,假如有两个物体,那么,我们只需要检测这两个物体是否相交即可,如果有3个物体a,b,c ,那么,我们要比较ab ac bc,如果有四个物体abcd,我们要比较ab ac ad bc bd cd,可见,有n个物体,我们要比较(n-1)+(n-2)……+(1)次,如果物体相当多,那么比较次数也会及其可怕

为了解决这个问题,unity里使用了**空间划分技术,**目前主流的划分技术有BSP,BHV,八叉树,四叉树

这几种算法都用到了树结构,下面分别简单介绍一下

BSP

BSP:BSP是一个二叉树结构,首先选定一个面作为根节点(一般会选两侧物体数量大致相同的面),然后遍历物体,如果物体在此面正面,将其加入到左子节点,如果在反面,加入到右子节点,如果和此面相交,加入左右两个子节点,之后,将左右子节点作为新的根节点进行递归

构建完成后,在检测时,只要检测同一叶子节点的所有物体就行了

unity碰撞检测 检测不到 unity碰撞检测原理_碰撞检测_03


这里注意,检测物体位于哪面一般使用长方体来进行近似的,大家用unity的时候可能也注意到,gameobject有width和height两个属性,这两个属性很可能是用来进行划分的

BHV

BHV:将相邻的物体放在一个圆内,检测时只要检测同一个圆内的

unity碰撞检测 检测不到 unity碰撞检测原理_子节点_04

八叉树,四叉树

unity碰撞检测 检测不到 unity碰撞检测原理_unity碰撞检测 检测不到_05


这两个其实原理相同,上面是四叉树,类比到3d空间就是八叉树这时,我们可以发现,如果碰撞体位移了,或者碰撞网格改变了,树的结构也会随之变化,这里我猜测,unity内部会有静态和动态两种树,静态树时预先计算好的,而动态树则会在有物体位移时重新计算,而动态树很可能也有相关优化,比如子物体多的物体会单独维护一个树,再将其作为动态树的子树

而这里有个问题,物体位移时树是会改变的,碰撞不会出现问题。但碰撞网格在运行时改变的话,碰撞会失效,如果我们动态生成一个物体,为其加入网格碰撞器,那么,它仍然不会有碰撞检测,可能是由于树没有更新,也有可能逼近算法没有执行,解决办法是,生成物体后,调用Setactive(false),然后Setactive(true)

在OnEnable里,会调用函数生成对网格进行碰撞检测所需的信息

unity碰撞检测 检测不到 unity碰撞检测原理_八叉树_06

接下来说一下连续碰撞检测的问题,上图说明了不同collisiondectection的物体是否进行连续检测

连续检测算法大概有下面几种,

一,比如说某物体的位移随时间变化为y=vt

unity里每次检测相隔0.02s,那么,假如上帧时间为0,这帧时间为0.02,引擎可以将0.002,0.004,0.006……0.018,0.02分别带入方程,只要有一个检测到就说明有碰撞,但是unity里用的并不是这一种

二,计算量最小,从此帧到上帧的位置连一条线,检测这条线有没有碰撞检测

三,unity里很可能就是这种,因为文档里说,连续检测只适用于内置的

unity碰撞检测 检测不到 unity碰撞检测原理_八叉树_07


此方法将物体的轨迹视为一个碰撞体进行检测,比如

unity碰撞检测 检测不到 unity碰撞检测原理_碰撞检测_08


将球体的轨迹近似为一个胶囊体,然后对胶囊体进行碰撞检测