效果演示
1.重叠检测
新生成的小球需要与数组中的每个小球进行判断是否重叠
判断依据:圆心距小于等于半径之和
2.碰撞检测
2.1 矢量分析
假设小球初速度方向为OA
经过碰撞以后它的速度为OA’
由于碰撞平面不是水平线,不方便计算,所以我们将整个球体模型沿碰撞平面旋转α角
旋转过后 OA --> OB OA’---->OB’
设OA = v
根据三角函数可得
x0 = v * cosα
y0 = v * sinβ
x1 = v * cos ( α + β)
= v * cosα * cosβ - v * sinα * sinβ
= x0 * cosα - y0 * sinα
y1 = v * sin ( α + β)
= v * sinα * cosβ + v * cosα + sinβ
= y0 * sinα + x0 * sinα
那么我们就可以获得 碰撞过后的速度
Vx’ = Vx * cosα- Vy * sinα
Vy’ = Vy * sinα+ Vx * sinα
// 旋转向量
function rotateVector(v, theta) {
return {
dx: v.dx * Math.cos(theta) - v.dy * Math.sin(theta),
dy: v.dx * Math.cos(theta) + v.dy * Math.sin(theta)
}
}
2.2 旋转角度
现在我们需要计算的是旋转的 α 角度为多少
根据正切函数 tan α = y / x 可得 α = arctan * y /x
在Math函数中用 atan2 来计算,返回弧度,是指x轴距离点(y,x)与圆心连线的夹角
结果为正表示从X 轴逆时针旋转的角度
结果为负表示从X 轴顺时针旋转的角度
因为小球碰撞不外乎就两种情况
1.一低一高
向量等于终点坐标减去起始坐标
向量AB = (xa - xb,ya - yb)
tan θ = (ya-yb)/(xa-xb)
|θ| = arctan (ya-yb)/(xa-xb)
xa < xb ----> xa - xb <0
ya < yb ----> ya - yb <0
所以(ya-yb)/(xa-xb) > 0
由于碰撞平面是沿着顺指针方向旋转 -θ = atan2(y,x)
为了θ为正 进行取反
θ = -atan2(ya-yb)/(xa-xb)
2.一高一低
同理
xa < xb ----> xa - xb <0
ya > yb ----> ya - yb >0
所以(ya-yb)/(xa-xb) < 0
由于碰撞平面是沿着逆指针方向旋转 θ = - atan2(y,x)
所以
θ = -atan2(ya-yb)/(xa-xb)
然后我们就可以根据角度进行旋转
let theta = -Math.atan2(p1.y - p2.y, p1.x - p2.x);
2.3 更新速度
根据动量守恒定律和动能定理我们有两个公式
将旋转过后的速度带入公式可得到新速度(旋转后)
在这里插入代码片
我们需要将新速度(旋转后)沿逆时针旋转α角,就可以获得小球
碰撞过后真正的速度
// 完全弹性碰撞计算新的速度(旋转后的坐标)
let v1RotatedAfterCollision = {
dx: (v1Rotated.dx * (p1.mass - p2.mass) + 2 * p2.mass * v2Rotated.dx) / (p1.mass + p2.mass),
dy: v1Rotated.dy
};
let v2RotatedAfterCollision = {
dx: (v2Rotated.dx * (p2.mass - p1.mass) + 2 * p1.mass * v1Rotated.dx) / (p1.mass + p2.mass),
dy: v2Rotated.dy
};
// 将新速度旋转回原来的角度获得真正的速度
let v1finalCollision = rotateVector(v1RotatedAfterCollision,-theta);
let v2finalCollision = rotateVector(v2RotatedAfterCollision,-theta);
//更新小球的速度
p1.dx = v1finalCollision.dx;
p1.dy = v1finalCollision.dy;
p2.dx = v2finalCollision.dx;
p2.dy = v2finalCollision.dy;
2.4 修复漏洞
最后我们需要解决一个小bug
我们需要对碰撞检测进行一个优化
多个球黏在一起
原因:
两个小球碰撞的时候还没有分开,检测函数就已经执行了很多次,导致速度被更新了很多次
需要判断当两个小球靠近的时候才判断碰撞
如果两个小球已经往不同的方向运动了,就不需要对这两个小球进行检测了
建立坐标系
根据向量积公式可得cos (a,b) = a