AABB重叠的核心逻辑是x轴和y轴投影均不分离,即(a.max.x >= b.min.x) && (b.max.x >= a.min.x) && (a.max.y >= b.min.y) && (b.max.y >= a.min.y),该式基于分离轴定理,简洁高效且默认将边缘接触视为碰撞。

判断 AABB 重叠的核心逻辑是「分离轴不成立」
两个 AABB 不重叠,当且仅当在 x 轴或 y 轴上完全分离。所以只要在两个轴上都「没分离」,就一定重叠。这不是几何直觉,而是分离轴定理(SAT)在轴对齐情况下的极简退化——你不需要算投影、不用叉乘,只比四次大小。
常见错误是写成 if (a.min.x b.min.x && ...) 这类混用大小关系的条件,容易漏掉边界相等(即边刚好贴合)是否算碰撞的语义问题。实际工程中,**边重合通常视为碰撞**(例如格子地图中角色贴墙应停止移动),所以要用 ≤ / ≥,而不是 。
- 正确判定 x 轴未分离:
a.min.x - y 轴同理:
a.min.y - 两轴同时未分离才返回
true;任一轴分离,立即返回false - 若使用浮点坐标,注意
==或直接比较可能受精度影响,但 AABB 重叠判定本身对 eps 敏感度低,一般无需额外容差(除非你明确要求“间隙小于 0.001 才算碰”)
用结构体封装时,min 和 max 的字段顺序不能反
很多人定义 AABB 为 struct AABB { float x, y, w, h; };,然后靠 x/y 是左下、w/h 是宽高来推导 min/max。这没问题,但一旦和别人代码对接或调用第三方库(如 SDL_FRect、Box2D 的 b2AABB),字段含义就容易错位。
更稳妥的做法是显式存 min 和 max:
struct AABB {
float min_x, min_y;
float max_x, max_y;
};
这样判定函数清晰无歧义:
bool overlaps(const AABB& a, const AABB& b) {
return a.min_x
- 如果非要用
x,y,w,h,务必统一约定:比如x,y是中心点,则min = x - w/2;如果是左上角,则min_y = y - h—— 混用会导致 y 轴判定恒错 - OpenGL 坐标系(y 向上)和多数 2D 引擎(y 向下)不影响 AABB 逻辑,因为重叠只依赖相对大小,不依赖方向
性能关键:早退出 + 内联 + 避免分支预测失败
这个函数会被每帧调用成百上千次(比如粒子碰撞、UI 矩形裁剪),所以不能只图写得短。x 轴分离的概率通常高于 y 轴(尤其在横向滚动场景),把 x 轴判断放前面能更快拒绝。
- 用
[[likely]](C++20)或编译器内置提示(如__builtin_expect)对「不重叠」分支做提示,但收益有限;优先保证逻辑清晰 - 必须声明为
inline或放在头文件里,避免函数调用开销 - 不要写成
return !(separated_on_x || separated_on_y);—— 多一次取反、多一个临时变量,还掩盖了早退出意图 - 如果批量检测(如四叉树遍历),考虑 SIMD 对四个 AABB 同时做 x 轴比较,但单次判定没必要过早优化
容易被忽略的边界:空矩形与 NaN 坐标
没人会故意传空矩形,但构造函数出错、输入数据损坏、除零后赋值都可能导致 min_x > max_x 或出现 NaN。此时上述判定会行为异常(NaN 恒为 false),导致本该碰撞的被跳过。
- 上线前加断言:
assert(a.min_x - 生产环境可加防御性检查:
if (std::isnan(a.min_x) || std::isnan(b.max_x)) return false; - 空矩形(
min == max)仍可参与判定:它退化为线段或点,只要另一矩形覆盖该点就算重叠 - 如果业务要求「面积为 0 不算碰撞」,需额外判断
(a.max_x - a.min_x) > 0 && (a.max_y - a.min_y) > 0,但这是业务逻辑,不是 AABB 数学定义
真正难处理的是跨坐标系拼接(比如 UI 层用像素坐标,物理层用米制单位),这时候光看 AABB 判定没用——得先做坐标转换。但那是另一个问题了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











