用平方距离比较可高效判断球体碰撞:若dx²+dy²+dz²≤(r₁+r₂)²则相交,若≤|r₁−r₂|²则包含;推荐全程用平方比较避免开方误差和性能损耗,并注意统一浮点类型与非负半径断言。

怎么用欧几里得距离判断球体碰撞
两个球体是否相交或包含,本质是看球心距离和半径之和/差的关系。C++ 里不需要第三方库,直接算 std::sqrt 或更稳妥地用平方比较(避免开方误差和性能损耗)。
设球 A 球心为 (x1, y1, z1)、半径 r1,球 B 为 (x2, y2, z2)、半径 r2,先算平方距离:dx*dx + dy*dy + dz*dz。
- 若该值 ≤
(r1 + r2) * (r1 + r2)→ 至少接触或相交 - 若该值 ≤
std::abs(r1 - r2) * std::abs(r1 - r2)→ 一球完全包含另一球(含内切) - 推荐全程用平方比较:避免浮点开方误差,也更快
为什么不能直接用 std::sqrt 比半径和
表面看 std::sqrt(dx*dx + dy*dy + dz*dz) 更直观,但实际有三个隐患:
-
std::sqrt在某些平台或优化级别下可能引入微小舍入误差,导致本该相交的球被判定为不相交 - 对大量球体做碰撞检测(如物理引擎)时,
sqrt是相对昂贵的运算 - 当
r1或r2为负(非法但未校验)时,r1 + r2可能为负,而距离永远 ≥ 0,逻辑直接错乱
所以工业级代码一律优先用平方距离比较,连 std::abs(r1 - r2) 都要先平方再比。
包含关系容易被忽略的边界情况
“包含”不是仅指小球在大球肚子里——内切(小球刚好贴着大球内壁)也算包含。但很多人只写 distance ,漏掉了等于的情况。
- 正确条件是:
sq_distance ,前提是已确保 <code>r1 >= r2 - 更健壮写法:用
sq_distance r2 ? (r1 - r2) : (r2 - r1)) * ((r1 > r2 ? (r1 - r2) : (r2 - r1))),或直接调std::pow(std::abs(r1 - r2), 2) - 如果两球半径相等,包含只发生在球心重合时(
sq_distance == 0),此时既是包含也是完全重叠
C++ 实现时要注意的类型与精度
用 float 还是 double 直接影响结果稳定性,尤其在高缩放或大坐标场景下:
- 若球心坐标是
float,半径也是float,请统一用float算平方距离,避免隐式转换引入额外误差 - 不要写
(r1 + r2) * (r1 + r2)时其中一个是int——比如r1 = 5(int)、r2 = 2.5f(float),结果可能是int溢出或精度丢失 - 调试时可加断言:
assert(r1 >= 0 && r2 >= 0),负半径无几何意义,早暴露比后期追 bug 强得多
真正麻烦的从来不是公式本身,而是坐标系单位不一致、半径没归一化、或者把左手系当右手系传进来的那种问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











