Math 计算两个移动球将碰撞的x/y点
我正在尝试(本质上)做一个简单的台球游戏,并且希望能够预测一个球一旦击中另一个球,它会去哪里 我认为,第一部分是计算主球是否会击中任何物体,如果击中了,会在何处碰撞。我可以算出一条线和一个球的碰撞点,但不能算出两个球的碰撞点 给定两个球的x/y位置和速度,我如何计算它们碰撞的点 (附言:我知道我可以通过计算每一步两个球之间的距离来做到这一点,但我希望得到更优雅、更优化的东西。) 设置示例:尝试计算红点Math 计算两个移动球将碰撞的x/y点,math,language-agnostic,collision-detection,physics,Math,Language Agnostic,Collision Detection,Physics,我正在尝试(本质上)做一个简单的台球游戏,并且希望能够预测一个球一旦击中另一个球,它会去哪里 我认为,第一部分是计算主球是否会击中任何物体,如果击中了,会在何处碰撞。我可以算出一条线和一个球的碰撞点,但不能算出两个球的碰撞点 给定两个球的x/y位置和速度,我如何计算它们碰撞的点 (附言:我知道我可以通过计算每一步两个球之间的距离来做到这一点,但我希望得到更优雅、更优化的东西。) 设置示例:尝试计算红点 需要注意的一些事情: 当两个球的半径r相撞时,它们的中心相距2r 可以假设第一个球沿直线运动
需要注意的一些事情:
- 当两个球的半径
相撞时,它们的中心相距r
2r
- 可以假设第一个球沿直线运动(好吧,第一个近似值,但从这里开始),并且可以找到此路径与从第一个球到第二个球的方向之间的角度,
alpha
- 你知道静止球的中心,不是吗李>
A
B
AB
A
沿移动方向构造光线R
B
构建一个半径为2r
的圆R
的B
放置一个线段,调用交点C
AB
,你可以找到AB
和R
之间的角度alpha
,用正弦定律找到BC
的长度D
,然后再次使用正弦定律计算距离ADBD
顺便说一句——如果两个球都在移动,这种构造将不起作用,但你可以将其转换为一个静止的帧,以这种方式求解,然后再转换回来。只需确保在反向转换后检查溶液是否在允许的区域内
/物理学家不可能做出这样的评论。我试图反抗。我真的做了。画出@dmckee的答案 编辑 作为对@ArtB necromancer回答的回应,上图中D点的解决方案可以写成:
1/2 {(Ax+Bx+2 d Dx Cos[alpha]- Dx Cos[2 alpha]+ 2 Dy (Cos[alpha]-d) Sin[alpha]),
(Ay+By+2 d Dy Cos[alpha]- Dy Cos[2 alpha]- 2 Dx (Cos[alpha]-d) Sin[alpha])
}
在哪里
及
嗯 我当时正在研究@dmckee的解决方案,我花了很多时间才完成。下面是我的笔记,也许是为了寻找一个更实际的答案,这是直接采取的 因此,荣誉属于他/她,但任何错误都是我的。我通常使用类似Pascal的赋值运算符(即
:=
)来区分显示我的工作和实际需要的代码。
我使用的是标准的Y=mX+b
格式和准oop表示法。我对线段和结果线都使用BC。也就是说,这应该是“几乎”可以复制粘贴的Python代码
(删除“;”,将“sqrt”和“sqr”替换为正确版本等)
AB.m:=(b.y-a.y)/(b.x-a.x)代码>AB.b:=A.y-AB.m*A.x代码>
R.m:=A.v.y/A.v.x代码>R.b:=A.y-R.m*A.x代码>
BC.m:=-A.v.x/A.v.y
是垂直坡度的标准方程式,BC.b:=b.y-BC.m*b.x代码>现在C
是AB
遇到BC
的地方,所以我们知道它们是相等的,所以让我们把C.y
soC.y==AB.m*C.x+AB.b==BC.m*C.x+BC.b
soC.x:=(AB.m-BC.m)/(BC.b-AB.b)
然后只需插入C.x
即可获得C.y:=AB.m*C.x+AB.b代码>
BC
,BC.l:=sqrt(sqr(B.x-C.x)+sqr(B.y-C.y))代码>
BC.l>A.r+B.r
,则存在零解,并且这些圆不接触,因为C
是A
的路径s相对于
B的近地点。如果
BC.l==A.r+B.r,则只有一个解决方案,并且
C==D。否则,如果
BC.l
有两种解决方案。你可以这样想,如果子弹漏掉了零个解,如果子弹擦伤了一个解,如果有两个解,那么进出口都有伤口。靠近A`的就是我们想要的
D
是AC
上的一个点,即a.r+B.r
(又称2r
)远离B,因此:
sqrt(sqr(D.x-B.x)+sqr(D.y-B.y))==2r
sqr(D.x-B.x)+sqr(D.y-B.y)==4*r*r
。现在,两个变量(即D.x
和D.y
)和一个等式是麻烦,但我们也知道D
是麻烦
在线AC
soD.y==AC.m*D.x+AC.b
Dx = Ax - Bx
Dy = Ay - By
d = Sqrt[4 r^2 - (Dx^2 + Dy^2) Sin[alpha]^2]/Sqrt[Dx^2 + Dy^2]
const ball = {
x, y, // position start of frame
r, // radius
vx, vy, // velocity vector per frame
}
// Two balls A, and B
A = {...ball, r: 20, etc}
B = {...ball, r: 20, etc}
u = (Bvx * (Ax - Bx) + Bvy * (Ay - By)) / ((Ax - Bx) ** 2 + (Ay - By) ** 2);
dA = (((Bx + Bvx * u) - Ax) ** 2 + ((By + Bvy * u) - Ay) ** 2) ** 0.5;
u = (Avx * (Bx - Ax) + Avy * (By - Ay)) / ((Bx - Ax) ** 2 + (By - Ay) ** 2);
dB = (((Ax + Avx * u) - Bx) ** 2 + ((Ay + Avy * u) - By) ** 2) ** 0.5;
Ar*Ar+Br*Br = (((Bx+Bvx*u)-Ax)**2+((By+Bvy*u)-Ay)**2)+(((Ax+Avx*u)-Bx)**2+((Ay+Avy*u)-By)**2);
f(u) = Ar*Ar+Br*Br-(((Bx+Bvx*u)-Ax)**2+((By+Bvy*u)-Ay)**2)+(((Ax+Avx*u)-Bx)**2+((Ay+Avy*u)-By)**2)
// return array if 0, 1, or 2 roots
Math.quadRoots = (a, b, c) => {
if (Math.abs(a) < 1e-6) { return b != 0 ? [-c / b] : [] }
b /= a;
var d = b * b - 4 * (c / a);
if (d > 0) {
d = d ** 0.5;
return [0.5 * (-b + d), 0.5 * (-b - d)]
}
return d === 0 ? [0.5 * -b] : [];
}
// For line segements
// args (x1, y1, x2, y2, x3, y3, x4, y4, r1, r2)
Math.interceptTime = (a, e, b, f, c, g, d, h, r1, r2) => {
const A = a*a, B = b*b, C = c*c, D = d*d;
const E = e*e, F = f*f, G = g*g, H = h*h;
var R = (r1 + r2) ** 2;
const AA = A + B + C + F + G + H + D + E + b*c + c*b + f*g + g*f + 2*(a*d - a*b - a*c - b*d - c*d - e*f + e*h - e*g - f*h - g*h);
const BB = 2 * (-A + a*b + 2*a*c - a*d - c*b - C + c*d - E + e*f + 2*e*g - e*h - g*f - G + g*h);
const CC = A - 2*a*c + C + E - 2*e*g + G - R;
return Math.quadRoots(AA, BB, CC);
}
const res = Math.interceptTime(A.x, A.y, A.x + A.vx, A.y + A.vy, B.x, B.y, B.x + B.vx, B.y + B.vy, A.r, B.r);
// remove out of range values and use the smallest as time of first contact
const time = Math.min(...res.filter(u => u >= 0 && u < 1));
// point of contact for balls A and B
ax = A.x + A.vx * time;
ay = A.y + A.vy * time;
bx = B.x + B.vx * time;
by = B.y + B.vy * time;