两个圆相交的充要条件是圆心距平方 ≤ (r₁ + r₂)²,外离是圆心距平方 > (r₁ + r₂)²,内含是圆心距平方 < (r₁ − r₂)²(r₁ ≥ r₂),内切和外切均需引入容差ε避免浮点误差导致的误判。

用距离平方判断比开方更高效
两个圆相交的充要条件是:圆心距离 ≤ 半径之和。但直接算 sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) 会引入浮点开方,性能差且有精度扰动。游戏循环中每帧可能调用成百上千次,必须避免 <code>sqrt。
实际做法是两边平方(所有值非负,不改变不等号方向):
dx = x1 - x2;
dy = y1 - y2;
r_sum = r1 + r2;
if (dx * dx + dy * dy <= r_sum * r_sum) {
// 相交(含内切、外切、相交、包含)
}注意:这个判断包含“一个圆完全在另一个圆内”的情况(即 distance ),如果业务需要排除包含、只检测“边缘相交”,得额外加一层判断。
区分“相交”“内含”“相离”三种状态
很多游戏逻辑不能只回答“是否相交”,比如粒子碰撞要反弹,而角色被护盾包裹时需判定“是否被完全覆盖”。这时需拆解距离与半径关系:
立即学习“C++免费学习笔记(深入)”;
- 若
dx*dx + dy*dy > (r1 + r2)*(r1 + r2)→ 相离 - 若
dx*dx + dy*dy (假设 <code>r1 >= r2)→ 内含(小圆完全在大圆内,无交点) - 其余情况 → 相交(含外切、内切、部分重叠)
特别注意:(r1 - r2)*(r1 - r2) 要用绝对值差的平方,否则 r2 > r1 时结果为负,导致误判。稳妥写法是 abs(r1 - r2) 再平方,或直接比较 dx*dx + dy*dy r2 ? (r1 - r2) : (r2 - r1)) * ...。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
浮点误差下如何处理“恰好相切”
理论上的外切(distance == r1 + r2)在浮点运算中几乎不会精确命中。直接用 == 判定相切必然失败。正确做法是引入容差(epsilon):
若你真需要识别“近似相切”(例如触发特殊音效),应判断:
float dist_sq = dx*dx + dy*dy;
float r_sum = r1 + r2;
float diff = fabsf(sqrtf(dist_sq) - r_sum); // 这里 sqrt 不可避,但仅在极少数判定时用
if (diff < 1e-4f) { /* 近似外切 */ }但更推荐全程用平方比较 + 容差区间,例如:
float r_sum_sq = (r1 + r2) * (r1 + r2);
float eps = 1e-6f;
if (dist_sq > r_sum_sq && dist_sq < r_sum_sq + eps) { /* 视为刚接触 */ }不过多数游戏物理不需要这么精细——只要进 相交 分支就做响应,相切本身不是独立状态。
结构体封装与成员函数建议
别裸写一堆 x, y, r 变量。定义 struct Circle 并提供 intersects(const Circle& other) const 方法,既清晰又利于复用:
struct Circle {
float x, y, r;
bool intersects(const Circle& o) const {
float dx = x - o.x;
float dy = y - o.y;
float r_sum = r + o.r;
return dx*dx + dy*dy <= r_sum * r_sum;
}
};如果项目用 SIMD 或需要批量检测(如大量子弹 vs 敌人),后续可扩展为 AoS 转 SoA,但单个检测没必要过早优化。真正容易被忽略的是:**半径必须是非负数**——传入负半径会导致 r_sum 异常,但编译器不会报错,运行时行为不可预测,建议构造函数中加 assert(r >= 0)。


















