用分离轴定理判断圆与旋转矩形碰撞:只需检测4个轴(矩形两邻边方向及圆心到矩形中心方向),计算投影区间是否重叠,任一轴无重叠即无碰撞,全部重叠才判定碰撞。

用分离轴定理(SAT)判断圆与旋转矩形碰撞
直接计算几何交点太慢又易错,实际工程中推荐用分离轴定理——它不求交点,只问“是否存在一个轴,让两图形在该轴上的投影完全不重叠”。只要找到一个分离轴,就判定无碰撞;所有候选轴都重叠,才认为碰撞。
对圆+旋转矩形组合,只需检查 4 个轴:矩形的两条边方向(即本地 x/y 轴旋转后的单位向量),再加上「从矩形中心指向圆心」这个方向(因为圆是各向同性的,它的“支撑点”方向必须包含圆心相对矩形的位置)。
-
std::cos和std::sin用于将矩形旋转角转为边方向向量,注意角度单位是弧度 - 圆在任意轴
axis上的投影区间是:[center · axis - radius, center · axis + radius] - 矩形在
axis上的投影区间由 4 个顶点点积得到,取min和max - 两个区间重叠当且仅当:
projA_min
先做快速包围盒剔除(AABB early-out)
如果圆心离旋转矩形中心太远,连最远顶点都够不着,根本不用进 SAT。用旋转矩形的轴对齐包围盒(AABB)粗筛能省下 70%+ 的计算开销。
- 先算出旋转矩形 4 个顶点坐标(基于中心、宽高、角度),再求它们的
min_x/max_x/min_y/max_y - 若
circle.center.x ,或类似地超出任一边,直接返回 false - 这步不要用
sqrt算距离——比较平方距离更高效:(dx*dx + dy*dy) > (rect_half_diag + radius)*(rect_half_diag + radius)
避免浮点精度导致的误判(尤其是圆紧贴边时)
当圆心几乎落在矩形某条边上时,点积计算可能因舍入误差让投影区间“看起来”不重叠,实际应视为碰撞。必须引入小阈值容差。
- 所有区间重叠判断要加
EPS = 1e-6f容差:projA_min - 不要用
==比较浮点投影边界,只比大小关系 - 若矩形非常扁(宽高比 > 100:1)或角度接近 90° 倍数,建议先把矩形局部坐标系正交归一化,再传入 SAT
C++ 实现关键片段(无依赖、单文件可跑)
struct Vec2 { float x, y; };
Vec2 operator+(Vec2 a, Vec2 b) { return {a.x+b.x, a.y+b.y}; }
float dot(Vec2 a, Vec2 b) { return a.x*b.x + a.y*b.y; }
<p>bool circleRotRectCollision(Vec2 circleCenter, float radius,
Vec2 rectCenter, float width, float height, float angle) {
const float EPS = 1e-6f;
// Step 1: AABB early-out
float w2 = width <em> 0.5f, h2 = height </em> 0.5f;
float ca = cosf(angle), sa = sinf(angle);
// AABB of rotated rect: project corners onto axes
float dx = fabsf(circleCenter.x - rectCenter.x);
float dy = fabsf(circleCenter.y - rectCenter.y);
float hx = w2 <em> fabsf(ca) + h2 </em> fabsf(sa);
float hy = w2 <em> fabsf(sa) + h2 </em> fabsf(ca);
if (dx > hx + radius || dy > hy + radius) return false;</p><pre class="brush:php;toolbar:false;">// Step 2: SAT on 4 axes
Vec2 u = {ca, sa}; // x-axis of rect
Vec2 v = {-sa, ca}; // y-axis of rect
Vec2 w = {circleCenter.x - rectCenter.x, circleCenter.y - rectCenter.y};
// Project circle: [c·axis ± radius]
auto circleProj = [&](Vec2 axis) -> std::pair<float> {
float d = dot(w, axis);
return {d - radius, d + radius};
};
// Project rect: min/max of 4 corners dot axis
auto rectProj = [&](Vec2 axis) -> std::pair<float> {
float p0 = -w2 * dot(u, axis) - h2 * dot(v, axis); // (-w2,-h2)
float p1 = w2 * dot(u, axis) - h2 * dot(v, axis); // (+w2,-h2)
float p2 = -w2 * dot(u, axis) + h2 * dot(v, axis); // (-w2,+h2)
float p3 = w2 * dot(u, axis) + h2 * dot(v, axis); // (+w2,+h2)
return {fminf(fminf(p0,p1), fminf(p2,p3)), fmaxf(fmaxf(p0,p1), fmaxf(p2,p3))};
};
for (auto& axis : {u, v, w}) {
auto [cmin, cmax] = circleProj(axis);
auto [rmin, rmax] = rectProj(axis);
if (cmax <p>}</p></float></float>注意 w 轴不是单位向量,但 SAT 对非单位轴依然成立(只是投影长度缩放,不影响重叠判断)。真正容易被忽略的是:矩形顶点在本地坐标系中是 (±w2, ±h2),必须用 u 和 v 做基底展开,不能直接套旋转矩阵算四个点再点积——那样多算 8 次三角函数。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











