最常见的原因是坐标系理解错误:矩形表示方式不统一(如混用left/right/top/bottom与x/y/w/h),或y轴方向定义不一致(如Direct2D中y向上而OpenGL纹理坐标y向下),导致边界计算错误。

为什么两个 rect 重叠了但 Intersects 还返回 false
最常见原因是坐标系理解错:AABB 检测默认假设矩形用「左上角 + 宽高」或「中心 + 半宽高」表示,但如果你传的是「左上 + 右下」却按「左上 + 宽高」算,边界就会偏移。比如 rect1.right = rect1.left + rect1.width 这个等式不成立时,整个检测就失效。
- 检查你的矩形结构是否统一用
left/right/top/bottom,还是用x/y/w/h—— 二者混用必出错 - 如果用
x/y/w/h,确保y是 top(Direct2D、SDL)还是 bottom(OpenGL 纹理坐标系);y方向反了会导致top和bottom判定颠倒 - 浮点比较不用
==,但 AABB 只需不等式,所以只要坐标值本身没 NaN 就没问题;真正要小心的是NaN传入后整个比较结果为false(连a 都是 <code>false)
AABB::Intersects 的标准写法(无依赖、可手敲)
核心就是四条边不分离:只要一个矩形完全在另一个的左边、右边、上边或下边,就不相交;否则相交。这是唯一可靠、无分支优化陷阱、兼容所有浮点/整数类型的写法。
bool Intersects(const Rect& a, const Rect& b) {
return !(a.right <= b.left || // a 在 b 左侧(含贴边)
b.right <= a.left || // b 在 a 左侧
a.bottom <= b.top || // a 在 b 上方(注意:bottom < top 表示 y 向下增长)
b.bottom <= a.top); // b 在 a 上方
}
- 用
而不是 <code>,保证边重合时也判为相交(大多数游戏/UI 场景需要“接触即碰撞”) - 如果坐标系是 y 向上(如数学坐标),把
top/bottom名称换成minY/maxY,逻辑不变,但别把变量名和实际含义搞反 - 这个函数不关心矩形是否合法(比如
width ),所以调用前应确保 <code>left 且 <code>top (或按你定义的顺序一致)
当矩形带旋转时,还能用 AABB 吗
不能。AABB 全称是 Axis-Aligned Bounding Box,关键词是 Axis-Aligned —— 一旦矩形旋转,它的包围盒必须重新计算为新的轴对齐矩形,而这个新矩形会变大,导致检测变松(误报增多)。你看到的“旋转矩形碰撞”要么是退化成 AABB 粗筛(再进精确检测),要么根本不是 AABB。
- 如果只是轻微旋转(比如 ±5°),用 AABB 包围原旋转矩形,再检测,可以接受一定误差;但不要指望它替代 OBB
- 想做精确旋转矩形碰撞?得用分离轴定理(SAT),涉及向量投影,复杂度高一个数量级,且必须保证输入是凸多边形
- Unity/Unreal 等引擎里
BoxCollider2D开启usedByComposite或设为 trigger 时,底层仍可能回退到 AABB;真要旋转检测,得切到PolygonCollider2D
性能关键:别在每帧反复构造 Rect 对象
如果你每次调用都从四个独立 float 临时组装 Rect(比如 Rect{obj.x, obj.y, obj.w, obj.h}),编译器未必能完全优化掉构造开销,尤其在嵌套循环或粒子系统中,这会成为热点。
立即学习“C++免费学习笔记(深入)”;
- 把
Rect作为对象成员缓存(如struct Entity { Vec2 pos; Vec2 size; Rect bounds; }),只在位置/大小变更时更新bounds - 如果用的是
std::array<float></float>或裸数组,确保内存布局连续(避免指针跳转),现代 CPU 对连续读取友好得多 - 批量检测时(如 1000 个物体两两检测),O(n²) 无法避免,但可以用空间划分(如网格桶)先过滤,AABB 本身只是其中一环,别把它当成万能加速器
right == left 的退化矩形、极大坐标值导致浮点精度丢失、负尺寸未校验——这些不会报错,但会让碰撞“有时生效有时不”,查起来极费时间。










