2D Elastic Collision:y方向momentum不守恒及垂直碰撞问题求助
2D弹性碰撞模型垂直碰撞失效问题修复
问题描述
我是一名高中生,正在构建两个台球的2D弹性碰撞模型,采用向量旋转方法实现,但遇到了问题:当两球x值相同(垂直碰撞)时,碰撞逻辑失效。排查发现碰撞向量越趋近于垂直,y方向动量变化量就越大,而理论上该变化量应为0,x方向动量则无此问题。
问题根源
代码中旋转回原坐标系时的计算顺序错误:在计算v1y时,已经修改了v1x的值,导致v1y使用了更新后的v1x而非原始旋转后的v1x,垂直碰撞时这个误差被放大,直接导致动量不守恒。
修复方案
修正旋转回原坐标系的计算逻辑
在旋转回原坐标系时,先保存原始的旋转后速度值,避免计算过程中变量被覆盖:# 替换原旋转回代码 # 先保存旋转后的原始值 rot_v1x = v1x rot_v1y = v1y rot_v2x = v2x rot_v2y = v2y # 重新计算旋转回后的速度 v1x = rot_v1x * cos(angle) - rot_v1y * sin(angle) v1y = rot_v1y * cos(angle) + rot_v1x * sin(angle) v2x = rot_v2x * cos(angle) - rot_v2y * sin(angle) v2y = rot_v2y * cos(angle) + rot_v2x * sin(angle)优化碰撞检测与位置修正
原位置修正逻辑可能导致球重叠或位置偏移,建议改为基于碰撞方向的分离:# 替换原位置修正代码 # 计算碰撞方向单位向量 dist = ((b2.x - b1.x)**2 + (b2.y - b1.y)**2)**0.5 overlap = 2*r - dist dir_x = (b2.x - b1.x)/dist dir_y = (b2.y - b1.y)/dist # 分离两球,避免持续触发碰撞 b1.x -= dir_x * overlap/2 b1.y -= dir_y * overlap/2 b2.x += dir_x * overlap/2 b2.y += dir_y * overlap/2完整修正后的
BallCollision函数def BallCollision(b1, b2, u1, u2): deltaX = b2.x - b1.x deltaY = b2.y - b1.y angle = atan2(deltaY, deltaX) # 旋转速度到碰撞坐标系 u1x = b1.vx * cos(angle) + b1.vy * sin(angle) u1y = b1.vy * cos(angle) - b1.vx * sin(angle) u2x = b2.vx * cos(angle) + b2.vy * sin(angle) u2y = b2.vy * cos(angle) - b2.vx * sin(angle) # 计算弹性碰撞后的速度(碰撞坐标系) v1x = ((b1.m - b2.m) / (b1.m + b2.m)) * u1x + ((2 * b2.m) / (b1.m + b2.m)) * u2x v1y = u1y v2x = ((2 * b1.m) / (b1.m + b2.m)) * u1x + ((b2.m - b1.m) / (b1.m + b2.m)) * u2x v2y = u2y # 分离重叠的球 dist = ((b2.x - b1.x)**2 + (b2.y - b1.y)**2)**0.5 overlap = 2*r - dist if overlap > 0: dir_x = (b2.x - b1.x)/dist dir_y = (b2.y - b1.y)/dist b1.x -= dir_x * overlap/2 b1.y -= dir_y * overlap/2 b2.x += dir_x * overlap/2 b2.y += dir_y * overlap/2 # 旋转回原坐标系(使用临时变量避免覆盖) rot_v1x, rot_v1y = v1x, v1y rot_v2x, rot_v2y = v2x, v2y v1x = rot_v1x * cos(angle) - rot_v1y * sin(angle) v1y = rot_v1y * cos(angle) + rot_v1x * sin(angle) v2x = rot_v2x * cos(angle) - rot_v2y * sin(angle) v2y = rot_v2y * cos(angle) + rot_v2x * sin(angle) # 验证动量守恒 print("x方向动量变化: ", b1.m*b1.vx + b2.m*b2.vx - b1.m*v1x - b2.m*v2x) print("y方向动量变化: ", b1.m*b1.vy + b2.m*b2.vy - b1.m*v1y - b2.m*v2y) b1.vx, b1.vy = v1x, v1y b2.vx, b2.vy = v2x, v2y
额外建议
- 动量计算时要乘以质量,原代码未考虑质量,导致验证结果不准确
- 避免在
motion函数中每次都调用checkCollision,建议在主循环中统一检测碰撞,减少重复计算 - 可以添加边界碰撞检测,让球在窗口内反弹,增强模拟真实性
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

