两圆发生碰撞的数学条件是圆心距平方小于半径之和的平方,即 $(x_2 - x_1)^2 + (y_2 - y_1)^2
判断两圆是否发生碰撞的数学条件是什么
两圆在2D空间中发生弹性碰撞,前提是它们位置发生重叠(即距离小于半径之和),且在上一帧尚未重叠。检测本身只需一次距离平方比较,避免开方运算:
设圆A中心为
(x1, y1)、半径r1,圆B为(x2, y2)、r2,则碰撞成立当且仅当:sqrt()
- 计算归一化碰撞法线:
nx = dx / sqrt(dist_sq),ny = dy / sqrt(dist_sq)- 计算穿透深度:
penetration = (r1 + r2) - sqrt(dist_sq)- 将A向反方向移
penetration * r1 / (r1 + r2),B向正方向移penetration * r2 / (r1 + r2)(按质量比分配,若等质量可简化为各移一半)关键点:分离必须在物理更新前完成,否则速度积分会加剧穿透;若跳过分离,后续动量交换会因位置错误而发散。
示例(等质量简化):
float dist = sqrt(dist_sq); float penetration = (r1 + r2) - dist; if (penetration > 0) { float nx = dx / dist, ny = dy / dist; x1 -= nx * penetration * 0.5f; y1 -= ny * penetration * 0.5f; x2 += nx * penetration * 0.5f; y2 += ny * penetration * 0.5f; }弹性碰撞后如何更新两圆的速度
使用二维完全弹性碰撞公式,假设无旋转、无外力,只考虑沿碰撞法线方向的动量守恒与动能守恒:
核心是将速度投影到法线(
n)和切线(t)方向:切向分量不变,法向分量按一维弹性碰撞公式交换。
- 先计算法线单位向量
n = (nx, ny),再构造正交切线单位向量t = (-ny, nx)- 对每个圆,分解速度:
v1n = v1.x * nx + v1.y * ny,v1t = v1.x * (-ny) + v1.y * nx- 法向速度更新(质量分别为
m1,m2):v1n_new = (v1n * (m1 - m2) + 2 * m2 * v2n) / (m1 + m2)v2n_new = (v2n * (m2 - m1) + 2 * m1 * v1n) / (m1 + m2)- 最后合成新速度:
v1_new = v1n_new * n + v1t * t若质量相等(
m1 == m2),公式退化为直接交换法向分量:v1n_new = v2n,v2n_new = v1n,实现最简。容易忽略的是:必须在分离完成后才更新速度;否则穿透状态下计算出的法线方向可能因数值抖动而翻转,导致速度突变。
实际工程中哪些细节最容易引发 bug
真实项目里,90% 的“碰撞后粘连”或“穿模飞走”问题,都来自这几个非算法层细节:
- 时间步长过大(如固定
dt = 1/30但物体速度很高):导致单帧位移远超半径,碰撞被跳过——应做连续碰撞检测(CCD)或子步进- 浮点精度累积:多次分离+速度更新后,
dist_sq可能略小于(r1+r2)^2却未被再次检测——建议加一个极小容差,如if (dist_sq- 未处理静止物体(如墙):把半径为0的“墙”当作圆处理时,
sqrt(0)或除零风险需单独分支- 坐标系混用:UI 坐标Y向下,而物理引擎Y向上,法线方向取反却没同步修正速度更新
真正难的从来不是公式本身,而是让它们在帧率波动、浮点噪声、多物体并发的现实环境里稳定跑下去。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!












