在2D游戏开发或者图形交互系统中,矩形碰撞检测是最基础也最高频的需求之一。AABB指的是轴对齐包围盒,也就是矩形的边始终与坐标轴平行,不需要考虑旋转。利用C++实现这种检测,核心思路非常直接:判断两个矩形在水平和垂直方向上的投影是否同时重叠。

一、AABB碰撞检测的基本原理
AABB全称Axis-Aligned Bounding Box,意为轴对齐包围盒。由于矩形不发生旋转,我们只要记录它在x轴和y轴上的区间范围,就能完整描述其位置与大小。最常见的表示方式有两种:一种是记录左上角坐标加上宽度和高度,另一种是记录最小点和最大点。无论哪种方式,碰撞的本质都是两个区间在x和y方向都有交集。
从数学上看,假设矩形A的x区间为[minAx, maxAx],矩形B的x区间为[minBx, maxBx],它们在x轴重叠的条件是 minAx < maxBx 且 maxAx > minBx。y轴同理。只有x和y两个条件同时成立,两个矩形才发生相交。这种判断方式不涉及任何开方或三角函数,计算代价极低,非常适合每帧执行成千上万次的场景。
二、C++中的基础结构体定义
在C++里,我们首先要把矩形抽象成数据结构。使用左上角加宽高的方式比较符合直觉,也方便与渲染接口对接。下面给出一个简单的结构体定义,并附带构造与获取边界的方法。
#include <iostream>
struct Rect {
float x; // 左上角x
float y; // 左上角y
float w; // 宽度
float h; // 高度
float minX() const { return x; }
float maxX() const { return x + w; }
float minY() const { return y; }
float maxY() const { return y + h; }
};
上面的代码把矩形表示为x、y、w、h四个浮点成员,并提供了min与max的访问函数。如果项目中习惯用最小最大点表示,也可以直接存minX、minY、maxX、maxY,两种方法在算法层面完全等价。使用结构体而非类的好处是内存布局紧凑,在批量检测时缓存命中率更高。
需要注意的是,宽度和高度在逻辑上应当非负。但在编辑器拖拽或物理反馈中,偶尔会出现负值,此时maxX可能小于minX。实际工程里可以在赋值处做标准化,或者检测函数中统一用最小值和最大值比较,避免逻辑漏洞。
三、核心碰撞检测函数实现
基于前面的结构体,我们可以写出一个最基础的AABB相交判断函数。它接收两个Rect对象,返回布尔值表示是否碰撞。
bool isCollide(const Rect& a, const Rect& b) {
// x轴区间重叠且y轴区间重叠才碰撞
bool overlapX = a.minX() < b.maxX() && a.maxX() > b.minX();
bool overlapY = a.minY() < b.maxY() && a.maxY() > b.minY();
return overlapX && overlapY;
}
这个函数只有四句比较和一次逻辑与,在现代CPU上几乎不耗时间。对于浮点数,由于精度问题,边界刚好贴合时可能出现微小误差,但普通的<和>判断已能满足绝大多数游戏和UI需求。如果需要包含边接触也算碰撞,可以把严格小于改成小于等于。
我们也可以写成只使用x、y、w、h的版本,减少函数调用。下面给出等价实现,更适合对性能极度敏感的内循环。
bool isCollideRaw(float ax, float ay, float aw, float ah,
float bx, float by, float bw, float bh) {
return (ax < bx + bw) && (ax + aw > bx) &&
(ay < by + bh) && (ay + ah > by);
}
四、批量检测与简单优化
当场景中有很多矩形时,如果对每个物体都和其余所有物体两两检测,复杂度会是O(n²)。在C++里可以用空间划分来降复杂度,例如把屏幕切成网格,只检测同一格或相邻格里的矩形。
#include <vector>
void checkAll(const std::vector<Rect>& list) {
for (size_t i = 0; i < list.size(); ++i) {
for (size_t j = i + 1; j < list.size(); ++j) {
if (isCollide(list[i], list[j])) {
std::cout << "Rect " << i << " hit " << j << std::endl;
}
}
}
}
上面的代码用i+1避免重复配对,是最简单的暴力解法。物体少时完全够用。若物体达到上千,就应该引入四叉树或均匀网格,把可能碰撞的集合缩小,再使用本节的函数做精确判断。
另一个常见优化是先用半径粗筛:给矩形算一个外接圆或中心距离,距离太远直接跳过AABB细判。这在物体分布稀疏时能省掉大量不必要的比较,是物理引擎里标准的broad phase做法。
五、与其他检测方式的对比
除了AABB,2D里还有圆碰撞、旋转矩形(OBB)以及像素级检测。圆碰撞只需比距离平方,比AABB还少两次比较,但只能近似方形物体。OBB能处理旋转,却要算投影轴和分离轴定理,成本高很多。像素检测最精确但完全不可用于实时。
| 检测类型 | 是否支持旋转 | 单次成本 | 适用场景 |
|---|---|---|---|
| AABB矩形 | 否 | 极低 | UI、瓦片、未旋转物体 |
| 圆形 | 否 | 低 | 子弹、角色粗略体 |
| OBB旋转盒 | 是 | 中高 | 刚体物理、赛车 |
| 像素检测 | 是 | 高 | 静态精确判定 |
从表中可以看出,只要美术资源本身不旋转,AABB就是性价比最高的方案。很多商业引擎在底层也先用AABB做粗筛,再换复杂算法算具体接触点。
在C++项目中把AABB封装成独立头文件,配合向量数学库,就能快速搭建出稳定的碰撞系统。理解它的区间重叠本质,也有助于迁移到3D的AABB盒以及后续的扫掠检测算法。