两圆碰撞的充要条件是平方距离 ≤ 半径和的平方,即 dxdx + dydy ≤ (r1 + r2)²,避免开方运算及浮点误差。

用圆心距离和半径和直接比较
两个圆碰撞的充要条件是:圆心之间的距离 ≤ 两圆半径之和。不需要开方,更不需要调用 sqrt——直接比较平方距离和半径和的平方,避免浮点开方运算和精度扰动。
常见错误是先算 sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) 再跟 r1 + r2 比,既慢又可能因 sqrt 的舍入误差导致边界 case 判定失准(比如刚好相切时误判为未碰撞)。
- 设圆 A 圆心
(x1, y1)、半径r1,圆 B 圆心(x2, y2)、半径r2 - 计算平方距离:
dx*dx + dy*dy,其中dx = x1 - x2,dy = y1 - y2 - 判断条件写成:
dx*dx + dy*dy
注意半径非负与浮点精度边界
如果输入半径可能为负(比如来自未校验的用户数据或配置),直接参与计算会导致逻辑反转。C++ 中 double 或 float 的比较还需防精度漂移——尤其当两圆几乎相切时,(r1 + r2)*(r1 + r2) 和平方距离非常接近,浮点误差可能翻转结果。
- 建议提前断言或 clamp 半径:
if (r1 - 若需鲁棒性更强,可加小偏移量:
dx*dx + dy*dy (仅限 <code>double场景) - 整数坐标+整数半径时完全可避免浮点误差,优先用
int运算
在游戏循环或物理系统中怎么高效调用
频繁调用该判定(如每帧检测几十个圆之间碰撞)时,函数内联和参数传递方式会影响性能。别把临时对象或结构体传进去再解包——直接传 float x1, float y1, float r1, ... 更快;现代编译器对这种简单函数基本都会自动 inline。
- 避免封装成
Circle类后重载operator==或写复杂成员函数,除非已有统一几何库抽象 - 若已用
struct Circle { float x, y, r; };,传 const 引用比值传递更安全,但注意结构体小(12 字节以内)时值传递反而更快 - 批量检测时,可先做粗筛(比如用 AABB 包围盒快速排除明显不交的圆),再进精确圆圆判定
和 AABB 碰撞检测混用时的典型陷阱
实际项目里常把圆形角色用 AABB 做初步剔除(比如视锥裁剪或空间格子索引),但要注意:圆的 AABB 是以圆心为中心、宽高均为 2*r 的正方形。若你用这个 AABB 去做格子哈希或四叉树插入,却在碰撞阶段仍用原始圆心+半径计算,没问题;但若中途修改了 AABB 的尺寸(比如加了 hitbox 缩放),而忘了同步更新半径,就会出现“视觉上没碰却触发”或“明明贴上了却不响应”的问题。
- 确保用于空间划分的包围盒尺寸和用于碰撞判定的
r1/r2来自同一数据源 - 调试时可临时画出每个圆对应的 AABB 边框,肉眼验证是否对齐
- 不要用
std::abs(x1 - x2) 替代圆判据——这是 AABB 重叠条件,比圆圆判定宽松得多,会产生大量误报
实际写的时候,最易被忽略的是半径符号检查和浮点临界值处理。哪怕逻辑再对,输入一个负半径就可能让整个碰撞系统静默失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











