分离轴定理(SAT)是判断两个凸多边形碰撞的可靠方法:只需检查各边法向量对应的有限条候选轴,若任一轴上投影不重叠则无碰撞;对凹多边形无效,须先凸分解;实操中需校验顶点数据并缓存单位法向量。

用分离轴定理(SAT)判断凸多边形碰撞最可靠
对两个不规则但**凸**的多边形,分离轴定理是标准解法:如果存在一条直线(轴),使得两多边形在该轴上的投影不重叠,则它们不相交;否则一定相交。关键在于——只需检查有限条候选轴,且每条轴都是某个多边形某条边的法向量。
注意:SAT 仅对**凸**多边形有效。若输入含凹多边形,结果不可信,必须先做凸分解或换算法(如网格扫描或GJK)。
实操建议:
- 对每个凸多边形,遍历其所有边,计算单位法向量(即
vec2(-dy, dx)归一化)作为候选分离轴 - 对每条轴,用点积将两个多边形的所有顶点投影到该轴上,得到两个标量区间
[min1, max1]和[min2, max2] - 用
max1 判断投影是否分离;一旦分离,立即返回 <code>false - 若所有轴都未分离,则返回
true
如何高效计算投影区间而不归一化法向量
归一化法向量会引入浮点误差和开方开销,实际中可跳过——因为投影比较只依赖相对顺序,不依赖绝对长度。只要用原始法向量(非单位)做点积,再比较区间端点即可。
立即学习“C++免费学习笔记(深入)”;
例如边向量为 edge = v[i+1] - v[i],对应法向量取 axis = vec2(-edge.y, edge.x),然后对每个顶点 p 计算 dot(p, axis),记录最小/最大值。
这样避免了 sqrt(),也减少了精度损失。但要注意:所有轴必须来自同一套“方向约定”(比如统一用左手法向),否则可能漏检。
常见错误:顶点顺序不一致或坐标系搞反
如果一个多边形顶点是顺时针排列,另一个是逆时针,法向量方向可能全反,导致投影区间错位,SAT 误判为不碰撞(假阴性)。
实操建议:
- 预处理时统一顶点顺序:用叉积累加判断绕向,必要时用
std::reverse()翻转顶点数组 - 确认 y 轴方向:图形 API(如 OpenGL)y 向上,而某些物理引擎或图像坐标 y 向下,法向量
vec2(-dy, dx)在 y 向下时应改为vec2(dy, -dx) - 调试时打印前几组投影值,看
min1/max1和min2/max2是否明显分离或重叠
C++ 实现需警惕的边界与性能点
两个 n 边形最多需检查 n + m 条轴,所以时间复杂度是线性的,但常数不小。真实项目中容易忽略的是浮点精度和退化情况。
实操建议:
- 不用
== 0判断分离,改用带 epsilon 的比较:max1 + eps ,eps 建议设为 <code>1e-6f(float)或1e-9(double) - 若某条边长度接近零(如重复顶点),跳过该边对应的轴,避免法向量为零向量
- 若多边形只有 2 个顶点(线段),需特殊处理为退化情况,或提前断言
size() >= 3 - 频繁调用时,把轴向量缓存为
std::vector<vec2></vec2>,避免每次重建
真正麻烦的不是算法本身,而是顶点数据来源不可控——读文件、网络传入、用户拖拽生成的点,往往隐含顺序错误、共线点、极小边长等问题。别假设输入“规范”,每次都要校验。


















