AABB碰撞判定应使用分离轴思想:仅需检查x、y轴投影是否重叠,任一轴分离即无碰撞;推荐用min_x/max_x命名并以<=判断相切,构造时强制归一化端点。

两个AABB矩形不发生碰撞,当且仅当它们在x轴或y轴上完全分离;反过来,只要x轴重叠且y轴也重叠,就一定碰撞。
为什么用“分离轴”而不是“求交”来判断
AABB是轴对齐的,没有旋转,所以只需检查两个方向:x轴投影是否重叠、y轴投影是否重叠。只要任一方向不重叠(比如rect1.right < rect2.left),就可立即返回false,无需计算交集区域——这比构造交集再判空快得多,也更安全(避免浮点误差下交集面积为负却未被察觉)。
常见错误是写成if (overlap_x && overlap_y) return true;但漏掉对坐标顺序的假设:必须确保每个矩形的left <= right、bottom <= top,否则比较结果会翻转。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 统一用
min_x/max_x代替left/right,显式表达区间端点语义 - 构造矩形时强制校验:
std::min(x1, x2)和std::max(x1, x2)初始化边界 - 若输入来自用户拖拽或传感器,务必先归一化:调用
normalize()函数交换错位端点
std::abs和std::max不能直接用于AABB碰撞判定
有人尝试用中心点距离判断:abs(center1.x - center2.x) < (half_width1 + half_width2),这看似简洁,但实际引入了冗余计算和精度风险:浮点减法+绝对值+加法,三步都可能累积误差;更重要的是,它隐含假设矩形对称,而AABB本身只定义边界,不依赖中心。
更糟的是用std::max(left1, left2) < std::min(right1, right2)这类写法——逻辑正确,但可读性差,且容易把max和min参数顺序写反(比如误写成std::max(right1, right2))。
推荐写法(清晰、易测、难出错):
bool intersects(const AABB& a, const AABB& b) {
if (a.max_x <= b.min_x || b.max_x <= a.min_x) return false;
if (a.max_y <= b.min_y || b.max_y <= a.min_y) return false;
return true;
}注意用<=而非<:边界刚好相切(如a.max_x == b.min_x)算碰撞,这是绝大多数物理引擎和UI系统的约定。
处理浮点坐标的边界陷阱
当矩形坐标是float或double时,a.max_x == b.min_x可能因舍入误差无法精确成立。此时严格相等判断会漏判“本应相切”的情况。
解决方案不是加epsilon暴力比较(那会扩大碰撞判定范围,引发误触),而是从设计上规避:如果业务允许,把坐标全转为整数像素(int)处理;若必须用浮点,则在归一化阶段做一次“安全收缩”:
// 归一化时微调,防止因计算误差导致 max < min a.min_x = std::min(a.x1, a.x2); a.max_x = std::max(a.x1, a.x2) + 1e-6f; // 向外撑一点,但仅限内部归一化
关键点:epsilon只用于修复自身计算误差,绝不用于跨矩形比较;所有碰撞主逻辑仍用原始<=判断。
真正容易被忽略的,是矩形数据来源是否可信——比如从JSON解析出的"left": 100.0000001和"right": 99.9999999,这种倒置必须在构造AABB对象时拦截,而不是等到intersects()里才发现逻辑崩坏。


















