本文翻译自 @sevenson 的文章 Separating Axis Theorem (SAT) Explanation 。原文作者用的是ActionScript 3来编写算法,不过文中主要讲述的还是算法原理,我想一旦算法原理被我们掌握了,选择什么编程语言来实现算法都是次要的事情了。 本人并非英文专业,所以文中翻译得有不妥或疏漏之处,欢迎各位指正,谢谢!
分离轴定理(英文简称SAT)是一项用于检测凸多边形碰撞的技术。
我绝不是这个方面的专家,但当检测碰撞的需求出现在我面前之后,我做了大量的阅读并最终在ActionScript 3中实现了它。
我想,我应该把我所学到的分享给大家,希望大家不会在这方面被坑得很惨:)
当我发现我需要在flash中检测多边形碰撞时,我碰巧地遇到了一个叫“分离轴定理”的方法。但唯一的问题是,为了真正地掌握它,我可费了不少功夫。
在阅读了大量有关碰撞检测的资料,并参看了一些代码示例后,这个方法总算被我领悟了。
为了帮助其他那些不精通数学的开发者,我想我应该写下这一篇能快速阐明这个算法工作原理的简短介绍。我还在下文引入了一个使用分离轴定理实现的demo,以及供大家下载并使用的ActionScript 3源代码。 (译者:demo和源代码请到原文中查看和下载)
注意:分离轴定理需要一点数学向量的知识,所以在深究这个算法前,你最好复习一下这方面的内容。
从根本上来讲,分离轴定理(以及其他碰撞算法)的用途就是去检测并判断两个图形之间是否有间隙。分离轴定理中用到的方法使算法本身显得十分独特。
我所听到过分离轴定理的最好类比方式是这样的:
假想你拿一个电筒从不同的角度照射到两个图形上,那么会有怎样的一系列的阴影投射到它们之后的墙壁上呢?
如果你用这个方式从每一个角度上对这两个图形进行处理,并都找不到任何的间隙,那么这两个图形就一定接触。如果你找到了一个间隙,那么这两个图形就显而易见地没有接触。
从编程的角度来讲,从每个可能的角度上去检测会使处理变得十分密集。不过幸运的是,由于多边形的性质,你只需要检测其中几个关键的角度。
你需要检测的角度数量就正是这个多边形的边数。也就是说,你所需检测的角度最大数量就是你要检测碰撞的两个多边形边数之和。举个例子,两个五边形就需要检测10个角度。
这是一个简易但比较啰嗦的方法,以下是基本的步骤:
步骤一:从需要检测的多边形中取出一条边,并找出它的法向量(垂直于它的向量),这个向量将会是我们的一个“投影轴”。
步骤二:循环获取第一个多边形的每个点,并将它们投影到这个轴上。(记录这个多边形投影到轴上的最高和最低点)
步骤三:对第二个多边形做同样的处理。
步骤四:分别得到这两个多边形的投影,并检测这两段投影是否重叠。
如果你发现了这两个投影到轴上的“阴影”有间隙,那么这两个图形一定没有相交。但如果没有间隙,那么它们则可能接触,你需要继续检测直到把两个多边形的每条边都检测完。如果你检测完每条边后,都没有发现任何间隙,那么它们是相互碰撞的。
这个算法基本就是如此的。
顺带提一下,如果你记录了哪个轴上的投影重叠值最小(以及重叠了多少),那么你就能用这个值来分开这两个图形。
在分离轴定理中,检测圆与检测多边形相比,会有点点奇异,但仍然是可以实现的。
最值得注意的是,圆是没有任何的边,所以是没有明显的用于投影的轴。但它有一条“不是很明显的”的投影轴。这条轴就是途经圆心和多边形上离圆心最近的顶点的直线。
在这以后就是按套路遍历另一个多边形的每条投影轴,并检测是否有投影重叠。
噢,对了,万一你想知道如何把圆投影到轴上,那你只用简单地把圆心投影上去,然后加上和减去半径就能得到投影长度了。
和其他的碰撞检测技术一样,分离轴定理算法有它自己的优点和不足。以下是其一些优点和不足的简要概述:
(译者:原来老外也喜欢先谈优点啊~>~)
可能这个算法会有更多优点和不足之处,但是我想这应该是最主要的几个了。
我希望这篇文章能帮助你了解到分离轴定理算法。我已经尽可能地不提供过多的信息并讲解得十分简明了。(我绝不是数学方面的专家,所以如果我遗漏了什么,我深表歉意)
以下是一些帮助我理解分离轴定理算法的页面:
我将文中的算法用JavaScript实现了一遍,大家有兴趣的话,可以到下面提供的链接中下载源代码或查看在线demo。