为了实现MOBA游戏,设置的简易物理碰撞计算库。
实现原理¶
- Unity先设置碰撞体,物理库负责获取对应的位置、碰撞体信息,减少图形化工作量
一.碰撞体¶
1.1 碰撞配置¶
- 碰撞类型
- 立方体Box
- 圆柱体Cylinder
- 位置信息mPos:PEVector3
- 立方体信息
- 碰撞体大小mmSize:PEVector3
- 轴向mAxis:PEVector3[3],三个向量来代表
- 圆柱体信息
- 半径mRadius:PEINT
- 碰撞体名字nName:string
1.2 碰撞体实现类¶
基类:位置、名字 立方体类:大小、轴向,根据配置文件构造的构造方法。 圆柱体类:半径、圆柱体类
二 确定性碰撞环境EnvColliders¶
数据:
- 碰撞体配置存储 List<envColliCfgLst>
- 碰撞体存储 List<ColliderBase>
环境初始化Init:获取场景中所有激活的碰撞器,创建确定性物理碰撞配置,并根据对应的碰撞体类型生成不同的碰撞体实现类(立方体、圆柱体)并存储。
三 碰撞检测流程CalcCollidersInteraction¶
1.0 确定运动的发生¶
如果速度为0,碰撞体不会运动,那就不会发生碰撞,直接返回。
1.1 预执行位移¶
游戏中的模拟是按帧来运行的,每一帧运动都是通过速度和加速度计算出的一段位移,而并非是现现实世界中一直移动直接碰到物体。当速度不为0,物体需要先直接根据速度直接发生位移,再进行碰撞检测。 $$ P = P +D_{ir} * V * multiper $$
物体需要先检测和哪些物体进行碰撞,才方便进行对应的处理。如何检测?
检测优化(暂时没做)¶
最简单方式:一个个遍历,会有所有的。 SAP(AABB)方式:根据AABB盒快速筛选。 四叉树划分:moba这种不用考虑Y轴的游戏没必要进行八叉树划分 - 重叠区域判断:对于在两个空间的区域,可以考虑同时存放到两个区域,或者存放到父节点(树结构不会碰撞,但这样可能会多判几个子区域的节点) - 松散四叉树:运动频繁的物体,可以使用松散四叉树提供出界(bound的一半size)和入界(原bound的大小)防止四叉树频繁更新。 八叉树划分*:八叉树会考虑空间Y轴上的划分。处理方式和四叉树类似。
1.2 碰撞位置纠正¶
物体可能下一帧直接穿墙而入,甚至直接穿过墙。所以我们必须先预判穿墙最近的点,并将其往回推离使其和碰撞体分离。
碰撞类型--圆柱与立方
¶
简要概括:通过位置偏移投影到轴上,再钳制大小,从而算出和目标碰撞体的相对位移算出P点。根据po向量计算长度和半径比较是否相交。相交则拿到po的标准化值进行推出 - 先计算位移差以及X、Z轴向距离:当前位置-目标位置即为位移差,轴向距离为其与对应轴向量的点乘 - 钳制距离:算出X轴距离后,将其钳制到立方体的X轴大小范围内。Z轴同理。这样才能拿到相交点。 - 计算轴向上的钳制投影向量:钳制投影再乘以轴向量 - 最终交点P:碰撞体中心位置 + 轴向偏移。 - 相交判断:计算PO向量->长度,长度用于判断是否相交,若po比半径大显然不相交。 (注意:PO的标准化向量即为法线,可设置成ref) - 边界调整:若相交。推回距离=圆半径长度-PO长度。同时返回true
碰撞类型--圆形与圆形¶
这个会简单更多,直接判断两圆之间的距离>半径和就可以得出。也不用再算交点,直接根据距离和半径的差即可实现位移。
- 法线:两物体位移差标准化后形成的向量。
更多碰撞算法(待完成)¶
这里只做了两个比较专的碰撞检测,更加复杂的方案应该选用下面的的方式进行碰撞计算。 - SAT:较为简单
多物体纠正位置¶
原理就是和所有的碰撞体纠正位移相加,但是后面会跟根据速度判断是否发生纠错,不然硬纠错会导致墙体穿透。
1.3 碰撞速度纠正¶
碰撞并非单纯的改变位置,原物体移动速度也应该发生变化。这个纠正和我们前面计算出来的法线强相关
物理材质¶
实际上纠正的速度应该与摩擦力和弹力有关,Unity可以设置物理材质:弹力和摩擦力。 - 弹力:代表了速度在法线反方向上的方向变化能力 - 摩擦力:代表了对于速度在垂直于法线方向上的的衰减能力
碰撞信息列表¶
前面圆和每个物体的碰撞,如果存在位置纠正,就说明发生了碰撞,单独生成一个有效碰撞信息CollisionInfo(对应碰撞体,法线、碰撞点) 放进一个列表中。我们拿到所有碰撞体信息列表进行纠正。 当列表大小为1时,直接进行简单的纠错,列表大于1时,要进行复杂的多物体纠错
单个碰撞体纠正速度¶
靠近纠正:由于我们只需要简单提供一个MOBA游戏需要的物理库,因此我们只需要提供一个默认的实现:当物体靠近碰撞体,删除速度在法线上的投影,保留剩下的速度(滑动速度)就好了。
确保远离:需要注意的一点是,如果在法线方向上,速度与法线同向,此时物体是在远离碰撞体运动,因此不需要删除该法线正方向上的速度投影(如下图)
多个碰撞体纠正速度(和位移)¶
首先,我们比较需要关注所有法线计算出的中心法线和偏离最大的法线(边缘法线)。
静止情况:如果速度v在aOb的范围之内,是不需要纠正速度的,因为不会这样的速度无论如何也不会向外滑动,保持静止在角落即可。
滑动情况:如果V在角度A向量和B向量的范围之外,此时需要找到离速度方向最大的那个法线,按照单一碰撞体纠错方式进行速度纠错。 - 中心法线计算:遍历所有法线(包括没有标出来的那几个发生了碰撞的物体),将其法线相加再除以个数,得到中心法线 - 最大夹角、边缘法线、边缘法线所在的碰撞信息:这块放一起计算,稍微有点复杂 - 遍历所有法线,计算其与中心法线的夹角。 - 取得最大值,并且记录此法线为最边缘法线(上图是A法线), - 遍历的同时记录对应速度和碰撞法线的最大夹角,更新纠正碰撞信息 - 最后传出中心法线、碰撞信息,并返回边缘-中心最大夹角用于判断是否纠错。 - 速度离中心法线角度:计算速度反方向(注意这里是在aob里面)和中心法线的夹角。 - 判断速度和位移纠正: - 当速度离中心法线角度 > 边缘法线离中心法线角度,才进行纠错(包括位移)。 - 反之,需要将其速度设置为0(不能保持原速度,否则会因为预执行的位移发生穿墙bug) - 回归速度纠错:最后根据前面的速度和碰撞信息中的法线,按照单一物体碰撞的方式进行纠错