结论:两个任意凸多边形碰撞判定不能仅依赖aabb粗筛,必须使用分离轴定理(sat);因aabb仅单向蕴含碰撞关系,易致误判或漏判,而sat通过检验所有边法向量投影区间重叠性实现精确判定。

直接说结论:两个任意多边形(尤其是凸多边形)是否碰撞,不能只靠AABB粗筛;真要判定,得用分离轴定理(SAT),否则漏判或误判概率很高。
为什么AABB intersects() 不能直接用于多边形碰撞
AABB只包住多边形的外轮廓,它本身是矩形。两个多边形没碰,但它们的AABB可能重叠;反过来,多边形真碰了,AABB也一定重叠——但这个“一定”只单向成立。也就是说,AABB::intersects() 只能当快速预筛,不能当最终判决。
- 常见错误现象:旋转后的矩形(OBB)或菱形、三角形等,用AABB判断时大量误报“碰撞”,实际图形完全没交
- 使用场景:UI控件、静态方块地图可用AABB;角色、子弹、动态障碍物必须进阶检测
- 性能影响:AABB是O(1),但跳过它直接上SAT,对上百个对象会明显卡顿
凸多边形用SAT检测的关键三步
SAT本质是找一条能把两个多边形彻底分开的直线(即分离轴)。找不到,就说明它们相交。对凸多边形,只需检查每条边的法向量方向即可。
- 第一步:取多边形A每条边的法向量(
Vector2(-dy, dx)或Vector2(dy, -dx),归一化非必须,但点积更稳) - 第二步:把A和B所有顶点分别投影到该轴上,得到两个标量区间:
[minA, maxA]和[minB, maxB] - 第三步:检查区间是否分离——若
maxA ,则当前轴是分离轴,直接返回 <code>false - 若所有边对应的轴都**没找到分离轴**,才返回
true
注意:两个凸多边形共需检查 A.size() + B.size() 条轴,不是所有组合,别写成双重循环。
projectAndCheckOverlap() 函数里最容易错的浮点细节
投影计算看着简单,但浮点误差会让本该分离的区间被判定为重叠,尤其在多边形几乎擦边时。
- 别用
==或直接比投影值;统一加一个 epsilon(如 <code>1e-6f)缓冲:用maxA + eps 判分离 - 点积实现必须一致:如果
dot(v, axis)返回的是带符号标量,就别在投影后取绝对值——方向错了整个区间就偏了 - 法向量方向不重要(正反都行),但A和B必须用**同一套方向约定**,否则
min/max逻辑会翻车 - 顶点顺序必须是逆时针或顺时针一致,否则法向量朝向混乱,轴集不全
凹多边形怎么办?别硬上SAT
SAT只对凸多边形数学保证正确。凹多边形直接套SAT会出错——比如一个“U”形和一根线段,SAT可能找不到分离轴,但其实没碰。
- 常见做法:把凹多边形三角剖分(如用ear clipping),再对每个三角形和另一多边形做SAT或点包含检测
- 更实用的路径:先用AABB粗筛,再对候选对用GJK算法(适合任意凸形,且支持穿透深度),但GJK代码复杂度高,调试成本大
- 工程取舍:多数2D游戏里,把角色/敌人建模为凸包(convex hull)比处理原始凹形更可靠、更快
真正难的不是写对SAT主干,而是顶点数据质量、浮点鲁棒性、以及什么时候该停在AABB、什么时候必须进SAT——这取决于你的物体形状有多“歪”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











