【Unity】碰撞检测算法及框架实现

背景

硕士期间研究课题是海洋生物数字孪生,基于各类Boids改进的算法里会有大量的海洋鱼类在三维空间中运动,鱼类之间会有互相感知的过程,同一帧里需要对许多行为进行决策判定,例如同伴鱼、食物、捕食者、栖息地等等。因此打算研究下有什么空间加速算法能够避免暴力迭代,减少开销。既然研究了这个,顺便也把碰撞检测也给弄懂了,想一次性弄的透彻一些,所以基于各个算法实现了一套碰撞检测框架。文章大纲和[1]这篇类似,包含了碰撞检测和空间加速算法。但是对各个算法的原理会简单概括,不对细节进行冗余介绍,但是会对各个算法的优缺点进行总结,并且主要侧重于框架的搭建

碰撞检测

碰撞体类型定义

所有2D碰撞体继承自Collider2D。

  • AABBCollider2D(继承自BoxCollider2D)
  • BoxCollider2D
  • SphereCollider2D
  • PolygonCollider2D

所有3D碰撞体继承自Collider。

  • AABBCollider(继承自BoxCollider)
  • BoxCollider
  • SphereCollider
  • MeshCollider

Collider2D和Collider不互相干涉。

各种碰撞检测算法

AABB算法(Axis-Aligned Bounding Box)

图片名称

OBB/SAT算法(Oriented Bounding Box / Seperating Axis Theorem)

OBB不赘述了,是简化成了四条边的SAT

  • 分离轴定理:两个凸多边形之间如果不相交,那么必定存在一条线可以将两者进行分割。这条线称之为分割线(Seperating Line)
  • 分离轴定理推论:两个凸多边形之间如果不相交,那么必定存在一条轴,让两多边形落在上面的投影线段不相交。这条轴称之为分离轴(Seperating Axis)

所以只要找到了那一条分离轴,就必定可以判断出两个凸多边形不相交。

但是这样找很难找,遍历的范围是无穷的,所以就有了简化的证明方法,来缩小搜索范围(重要推论

  • 重要推论:两个凸多边形之间如果不相交,那么必定存在一条平行于这两个多边形中的某一条边的分割线同时有一条垂直于该分割线的分割轴,让两多边形落在上面的投影线段不相交。
图片名称

其实AABB、OBB、SAT我认为都有个共同点,它们都是:只要寻找到一个轴,落在上面的投影没有交叉(Amin > Bmax || Amax < Bmin),则整体都不会有相交。

GJK算法(Gilbert–Johnson–Keerthi 人名缩写)

定理(基本概念)

  • 两个多边形相交 <=> 则它们的闵可夫斯基差集必然包括原点(零向量)
    这条定理的原因可以看下面的闵可夫斯基差集的基本概念
  • A和B是凸多边形 => A和B的闵可夫斯基差集也一定是凸多边形

闵可夫斯基差集(基本概念)

图形A和图形B中的所有点坐标两两相减,得到的新集合叫闵可夫斯基差集。比如图形A有3个点,图形B有4个点,那得到的结果一共有12个点。

图片名称

上图中只给了各个顶点相减,但是实际上把A上的所有点和B上的所有点相减所得到的点,一定会被包含在右图形成的凸包当中,证明略去。那么也可以看出来,如果A和B有相交的部分,那它们相减一定等于零向量,也就代表右图的闵可夫斯基差集中一定包含原点。于是问题就转化成了判断凸多边形内是否含某个点,而凸多边形一定可以拆成多个三角形,所以最终问题的最最核心的点就是:
①求三角形内部是否包含原点
②按什么顺序在这个凸多边形中去划三角形,一步步接近原点

单纯形(Simplex,基本概念)

K维单纯形就是包含K+1个点的凸多面体。二维下的单纯形就是三角形,三维下的单纯形的就是四面体。

Support函数(基本概念)

输入为方向d,输出是该方向上凸多边形的最远顶点的位置。

图片名称

算法过程

第一个三角形的构建:
(1)第一个点:先随机选一个方向direction(例如朝右或朝两个图形的中心点连成的向量),找到direction方向下图形A最远的点和-direction方向下图形B最远的点,相减得到的向量值作为第一个点的坐标。
(2)第二个点:选取-direction方向(在有的文章视频里,也会选择从第一个点指向原点的方向),找到-direction方向下图形A最远的点和direction方向下图形B最远的点,相减得到的向量值作为第二个点的坐标。
(3)第三个点:选取第一个点和第二个点连线的垂直向量作为新的方向,且这个垂直向量是指向原点在的那一侧的。同样找到这个方向下图形A最远的点和反方向下图形B最远的点,相减得到的向量值作为第三个点的坐标。

图片名称

原点没有被包含进三角形,则寻找下一个三角形:
如图,判断线段①③和线段②③哪一侧包含了原点。如果在①③的外侧,则往右找新的最远的点重新连三角形,如果在②③的外侧,则往左找新的最远的点连三角形。

图片名称

每个点在被加入的过程中同时考虑:在当前寻找方向direction下图形A和反方向下图形B的差所在的点,也就是闵可夫斯基差所形成的凸多边形在这个方向下的最远坐标。它是否超过了原点,如果没有超过,直接可以判断无法相交,这也是这个算法中唯一的无相交的结束条件。例如第一个三角形构建过程中的第三步,见下图中分析。

图片名称

实现效果

相关算法

判断一个点在一条线的顺时针侧(右)还是逆时针侧(左)

图片名称

判断一个点是否在一个三角形内

面积法

图片名称

向量法

无论三角形的连线方向是顺逆时针。只要点在三条边的同侧,就代表点在三角形内部。

判断一个图形是凹是凸

定义1:凸多边形是指如果一个多边形的所有边中,任意一条边向两方无限延长成为一直线时,其他各边都在此直线的同旁,那么这个多边形就叫做凸多边形。
定义2:所有的内角都是小于等于180°的角的多边形就是凸多边形。

图片名称

判断射线是否会穿过一条线段

图片名称

切割凹多边形为多个凸多边形[5]

图片名称

碰撞检测算法的切换条件

为了提高效率,在实际运行过程中,根据双方的碰撞体类型,增加了动态的算法切换。加粗代表已实现。

  • Sphere:两者都是Sphere时。
  • AABB(可被OBB代替):两者都是未发生旋转的Box,或者本身就是限制旋转的AABBBox。
  • AABB-圆[3,4]:两者其中一者是未发生旋转的Box,或者本身就是限制旋转的AABBBox,另一者是Sphere。
  • OBB(可被SAT代替):两者都是Box,且其中任意一者存在旋转。
  • SAT-多边形-多边形:两者都是凸多边形,且两者都不是Sphere。
  • SAT-多边形-圆:两者中其中一者是凸多边形,另一者是圆形。
  • GJK:两者中其中一者是凸多边形。
  • EPA:两者中其中一者是凸多边形。
    以上SAT、GJK、EPA三选一
  • 圆框与AABB矩形框(矩形框不旋转):其中一者是未发生旋转的Box,或者本身就是限制旋转的AABBBox。另一者是Sphere。
  • 分割SAT:两者其中一者是凹多边形,且两者都不是Sphere。

空间划分加速算法

四叉树

框架设计及搭建

参考

[1] https://blog.csdn.net/weixin_42186870/article/details/136147295
[2] https://zhuanlan.zhihu.com/p/177006015
[3] https://www.zhihu.com/question/266499219/answers/updated
[4] https://www.cnblogs.com/zhjblogs/p/16737750.html
[5] https://blog.csdn.net/qq_42883222/article/details/136858455

附加:Unity的碰撞检测介绍

在Unity 5.5之前,Unity使用SAT来进行碰撞检测。之后Unity推出了PCM(持久接触流形,Persistent Contacts Manifold)方法,会更高效,无用的碰撞点信息会更少。

现在的项目默认都是PCM了,但是也可以在Project Settings里修改:

图片名称
posted @ 2024-07-12 19:31  JimmyZou  阅读(271)  评论(0编辑  收藏  举报