计算 2 个移动球将发生碰撞的 x/y 点

发布于 2024-09-18 08:49:25 字数 384 浏览 9 评论 0原文

我正在尝试制作一个(本质上)简单的台球游戏,并且希望能够预测一旦击中另一个球就会去哪里。

我认为,第一部分是计算母球是否会击中任何物体,如果击中,则碰撞在哪里。我可以算出一条线和一个球的碰撞点,但不能算出两个球的碰撞点。

那么给定 2 个球的 x/y 位置和速度,我如何计算它们碰撞的点?

(PS:我知道我可以通过计算沿途每一步两个球之间的距离来做到这一点,但我希望有一些更优雅和更优化的东西。)

设置示例:尝试计算红点

http://dl.dropbox.com/u/6202117/circle.PNG

I'm trying to make what is (essentially) a simple pool game, and would like to be able to predict where a shot will go once it hits another ball.

The first part is, I believe, to calculate if the cueball will hit anything, and if it does, where it collides. I can work out collision points for a line and a ball, but not 2 balls.

So given the x/y positions and velocities of 2 balls, how do I calculate the point at which they collide?

(PS: Im aware I can do this by calculating the distance between the two balls every step along the way, but I was hoping for something a little more elegant and optimal.)

Example of setup: trying to calculate the red dot

http://dl.dropbox.com/u/6202117/circle.PNG

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

高跟鞋的旋律 2024-09-25 08:49:25

需要注意的一些事项:

  • 当两个半径为 r 的球发生碰撞时,它们的中心相距 2r
  • 可以假设您的第一个球沿直线行进(好吧,第一个近似值,但从这里开始),您可以找到该路径与从第一个球到球的方向之间的角度 alpha 。第二个。
  • 你知道静止球的中心,不是吗?

现在您需要做一些几何工作。

进行此构造:

  1. 将第一个(移动的)球的当前中心标记为点 A
  2. 将静止球的中心标记为点 B。
  3. 构造线段AB
  4. A 沿运动方向构造射线 R
  5. 围绕 B 构建一个半径为 2r 的圆。
  6. B 垂直于 R 放下一条线段,称为交点 C
  7. 您知道距离 AB,并且可以根据正弦定理找到 ABR 之间的角度 alphaBC的长度。
  8. 根据该长度确定是否有 0、1 或 2 个解。如果有 0 或 1 个,则完成。
  9. 在圆与靠近 A 的 R 交汇处构造点 D,并再次使用正弦定理求出距离 AD。
  10. 碰撞点是BD的中点

,现在你知道了一切。

由此构建有效的代码作为练习。


顺便说一句——如果两个球都在移动,这种结构就不起作用,但你可以转变成一个静止的框架,以这种方式解决它,然后再转回来。只要确保在逆向变换之后检查解是否在允许的区域内...

/物理学家不能发表这样的评论。我试图反抗。我真的做到了。

Some things to take note of:

  • When two balls, each of radius r collide their centers are 2r apart.
  • Your first ball can be assumed to travel in a straight line (well, first approximation, but start with this), and you can find the angle, alpha between this path and the direction from the first ball to the second.
  • You know the center of the stationary ball, no?

Now you have some geometry to do.

Do this construction:

  1. Mark the current center of the first (moving) ball as point A.
  2. Mark the center of the stationary ball as point B.
  3. Construct line segment AB.
  4. Construct the ray, R, from A in the direction of movement.
  5. Construct a circle of radius 2r around B.
  6. Drop a segment from B perpendicular to R call the point of intersection C.
  7. You know the distance AB and you can find the angle alpha between AB and R, with the Law of Sines find the length of BC.
  8. From that length determine if there are 0, 1 or 2 solutions. If there are 0 or 1 you are done.
  9. Construct point D where the circle meets R closer to A, and use the Law of Sines again to find the distance AD.
  10. The point of collision is the midpoint of BD

and now you know everything.

Constructing efficient code from this is left as an exercise.


BTW-- This construction won't work if both balls are moving, but you can transform into a frame where one is stationary, solve it that way, then transform back. Just be sure to check that the solution is in the allowed area after the reverse transformation...

/ Physicists can't not make comments like this. I tried to resist. I really did.

谁的年少不轻狂 2024-09-25 08:49:25

@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])
     }  

Where

Dx = Ax - Bx 
Dy = Ay - By   

And

d = Sqrt[4 r^2 - (Dx^2 + Dy^2) Sin[alpha]^2]/Sqrt[Dx^2 + Dy^2]  

HTH!

Drawing of @dmckee's answer

alt text

Edit

Just in response to @ArtB necromancer's answer, the solutions for point D in the above graph could be written:

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])
     }  

Where

Dx = Ax - Bx 
Dy = Ay - By   

And

d = Sqrt[4 r^2 - (Dx^2 + Dy^2) Sin[alpha]^2]/Sqrt[Dx^2 + Dy^2]  

HTH!

爱你是孤单的心事 2024-09-25 08:49:25

我正在查看 @dmckee 的解决方案,我花了很多功夫才完成它。以下是我的笔记,供那些可能正在寻找更实际答案的人使用,直接采取
来自他的帖子,因此功劳归于他/她,但任何错误都是我的。我通常使用类似 Pascal 的赋值运算符(即 :=)来区分显示我的工作和实际必要的代码。
我使用标准 Y = mX +b 格式和准oop表示法。我确实对段和结果线使用 BC。也就是说,这应该“几乎”是可复制粘贴的 Python 代码
(删除“;”,用正确的版本替换“sqrt”和“sqr”等)。

  1. Ax 和 Ay 是 x & y 位置,Ar 是 A 的半径,Av 是速度,Avx 是它的 x 分量,Avy 是它的 y 分量。
  2. B 是相同的,但没有速度(或更准确地说,从 A 中减去 B 的速度,因此 B 相对而言是静止的)。
  3. AB.m := (by - ay) / (bx - ax); AB.b := Ay - AB.m * Ax;
  4. Rm := Avy / Avx; Rb := Ay - Rm * Ax;
  5. 不需要
  6. BC.m := -Avx/Avy;这是垂直斜率的标准方程, BC.b := By - BC.m * Bx; 现在 CAB 满足 < code>BC 所以我们知道它们是相等的,所以让 Cy 相等,所以 Cy == AB.m * Cx + AB.b == BC.m * Cx + BC .b; 所以 Cx := (AB.m - BC.M) / (BC.b - AB.b); 然后插入 Cx得到 Cy := AB.m * Cx + AB.b;
  7. 你可以忽略正弦定律,因为我们有 AB 和 BC,所以我们可以使用毕达哥拉斯定理来获得 的长度BC, BC.l := sqrt( sqr(Bx-Cx) + sqr(By-Cy) );
  8. 如果 BC.l > Ar + Br,解为零,并且这些圆不接触,因为 CA 的路径相对于的近地点代码>B<代码>。如果BC.l == Ar + Br,则只有一个解,C == D。否则,如果 BC.l < Ar + Br然后就有
    有两种解决方案。你可以这样想,如果子弹错过了零个解决方案,如果有一个子弹擦过,如果有两个,则同时存在入口和出口伤口。更接近
    A`的就是我们想要的。
    1. 现在,数学变得很糟糕,所以我会展示我的工作,以防万一我做错了什么。
    2. DAC 上距离 B Ar + Br(又名 2r)的点,因此:
      sqrt( sqr(Dx - Bx) + sqr(Dy - By) ) == 2r
    3. 因此sqr(Dx - Bx) + sqr(Dy - By) == 4*r*r。现在 2 个变量(即 DxDy)与一个方程是很麻烦的,但我们也知道 D
      AC 线上,所以 Dy == AC.m*Dx + AC.b
    4. 我们可以替换 Dy,给出 sqr(Dx - Bx) + sqr(AC.m*Cx + AC.b - By) == 4*r*r.
    5. 这可以扩展为可爱的:sqr(Dx) + 2*Dx - sqr(Bx) + sqr(AC.m*Dx) + 2*AC.b*Dx - 2*AC.m*Dx *By + sqr(AC.b) - 2*AC.b*By + sqr(By) == 4*r*r是我最有可能制作的部分如果我确实犯了一个错误)。
    6. 我们可以收集这些项(请记住,此时只有 Dx 未知;其余的我们可以将它们视为常量)以获得
      sqr(Dx) + 2*Dx - sqr(Bx) + sqr(AC.m*Dx) + 2*AC.b*Dx - 2*AC.m*Dx*By + sqr(AC.b) - 2*AC.b*By + sqr(By) == 4*r*r
    7. 重写为更整洁的
      <代码>(sqr(Dx) + sqr(AC.m*Dx)) + ( 2*Dx + 2*AC.b*Dx - 2*AC.m*By*Dx ) + ( - sqr(Bx) + sqr(AC.b) - 2*AC.b*By + sqr(By) ) == 4*r*r 可以重构为
      <代码>(1 + sqr(AC.m)) * sqr(Dx) + 2*( 1 + AC.b - AC.m*By ) * Dx + ( sqr(By) - sqr(Bx) + sqr( AC.b) - 2*AC.b*By - 4*r*r ) == 0
    8. 这现在非常适合二次公式(即 x == (-bb +- sqrt( sqr(bb) - 4*aa*cc ) / 2*aa)(使用 aa 以避免与之前的变量混淆),使用 aa := 1 + sqr(AC.m);, bb := 2*( 1 + AC.b - AC.m*By );cc := sqr(By) - sqr(Bx) + sqr(AC.b) - 2*AC.b*By - sqr(A.r+ Br);.
    9. 现在我们可能会得到两个解决方案,因此让我们使用 -bb/2aa +- sqrt(sqr(bb)-4*aa*cc)/2*aa 保存部分:first_term := -bb/(2*a);second_term := sqrt(sqr(bb)-4*aa*cc)/2*aa;
    10. 第一个解决方案 D1.x = first_term + secondary_term;D1.y = AC.m * D1.x + AC.b,以及第二个解决方案 D2.x = 第一个项 + 第二个项;D2.y = AC.m * D2.x + AC.b
    11. 求到 A 的距离:D1.l := sqrt( sqr(D1.xA.x) + sqr(D1.yA.y) ); 以及D2.l = sqrt( sqr(D2.xA.x) + sqr(D2.yA.y) ); (实际上跳过两个平方根效率更高,但没有区别) .
    12. 更接近的是您想要的 D := D1 if D1.l < D2。 l 否则D2;
    1. DB 的中点,我们称之为 E,就是碰撞(如果半径不相等,我不知道这如何概括)。
    2. 因此构造行 DB.m := (By-Dy)/(Bx-Dx);DB.b = By - DB.m*Bx;
    3. 我们不需要长度来确定长度,因为它应该是 BD.l == Ar + Br,因此 sqrt( sqr(Ex-Bx) + sqr(Ey -By) ) == Br.
    4. 同样,我们可以替换 E,因为我们知道它在 BD 上,所以 Ey == BD.m * Ex + BD.b ,得到 sqrt( sqr(Ex-Bx) + sqr(BD.m * Ex + BD.bB.y) ) == Br
    5. 展开为 sqr(Ex) - 2*Ex*Bx + sqr(Bx) + sqr(BD.m)*sqr(Ex) + 2*BD.m*Ex*BD.b - 2* BD.m*By + sqr(By) - 2*BD.b*By + sqr(By) == sqr(Br)
    6. 收集到

    sqr(Ex) + sqr(BD.m)*sqr(Ex) + 2*BD.m*Ex*BD.b - 2*Ex*Bx + sqr(Bx) - 2*BD。 m*By + sqr(By) - 2*BD.b*By + sqr(By) == sqr(Br)
    <代码>(1 + sqr(BD.m)) * sqr(Ex) + 2*(BD.m*BD.b - Bx) * Ex + sqr(Bx) + sqr(By) - 2*BD.m *By + sqr(By) - 2*BD.b*By + sqr(By) - sqr(Br) == 0
    aa := (1 + sqr(BD.m)); bb := 2*(BD.m*BD.b - Bx); cc := sqr(By) - 2*BD.m*By + sqr(By) - 2*BD.b*By + sqr(By) - sqr(Br);,二次公式,然后得到你的两个点并选择更接近 B 的一个。

嫖娼,但我真的需要解决这个问题并认为我会分享。呃,我想我现在需要躺下。

I was looking at @dmckee's solution and it took me quite a lot of work to work through it. The following is my notes for those perhaps looking for a more practical answer, it is taken directly
from his post so the credit goes to him/her, but any mistakes are mine. I usual a Pascal-like assigment operator (ie :=) to distinguish between showing my work and the actual necessary code.
I'm using the standard Y = mX +b format and a quasi-oop notation. I do use BC for both the segment and the resulting line. That said, this should 'almost' be copy'n'paste-able Python code
(remove ";", replace "sqrt" and "sqr" with proper versions, etc).

  1. A.x and A.y are the x & y positions, A.r is A's radius, and A.v is the velocity with A.v.x being the x component of it and A.v.y being the y component of it.
  2. B is the same but without the velocity (or more accurately, subtract the velocities of B from A so B is relatively speaking stationary).
  3. AB.m := (b.y - a.y) / (b.x - a.x); AB.b := A.y - AB.m * A.x;
  4. R.m := A.v.y / A.v.x; R.b := A.y - R.m * A.x;
  5. not necessary
  6. BC.m := -A.v.x/A.v.y; which is the standard equation for perpendicular slope, BC.b := B.y - BC.m * B.x; Now C is where AB meets BC so we know that they are equal so lets equate C.y so C.y == AB.m * C.x + AB.b == BC.m * C.x + BC.b; so C.x := (AB.m - BC.M) / (BC.b - AB.b); then just plug in C.x to get C.y := AB.m * C.x + AB.b;
  7. You can ignore the sine law since we have AB and BC so we can just use the Pythagorean theorem to get the length of BC, BC.l := sqrt( sqr(B.x-C.x) + sqr(B.y-C.y) );
  8. If BC.l > A.r + B.r, there are zero solutions, and these circles do not touch since C is A's paths perigee with respect toB. IfBC.l == A.r + B.r, there is only one solution, andC == D. Otherwise, ifBC.l < A.r + B.rthen there
    are two solutions. You can think of this as such, if there are zero solutions the bullet missed, if there is one the bullet grazed, and if there are two then there is both an entry and exit wound. The one closer to
    A` is the one we want.
    1. Now, the math gets ugly so I'll show my work just in case I do something wrong.
    2. D is a point on AC that is A.r + B.r (aka 2r) away from B so:
      sqrt( sqr(D.x - B.x) + sqr(D.y - B.y) ) == 2r
    3. Therefore sqr(D.x - B.x) + sqr(D.y - B.y) == 4*r*r. Now 2 variables (ie D.x and D.y) with one equation is trouble, but we also know that D is
      on the line AC so D.y == AC.m*D.x + AC.b.
    4. We can substitute for D.y giving sqr(D.x - B.x) + sqr(AC.m*C.x + AC.b - B.y) == 4*r*r.
    5. This expands into the lovely: sqr(D.x) + 2*D.x - sqr(B.x) + sqr(AC.m*D.x) + 2*AC.b*D.x - 2*AC.m*D.x*B.y + sqr(AC.b) - 2*AC.b*B.y + sqr(B.y) == 4*r*r (this is the part where I mostly likely made a mistake if I did at all).
    6. These terms we can collect (remember, at this point only D.x is unknown; the rest we can treat as if they were constants) to get
      sqr(D.x) + 2*D.x - sqr(B.x) + sqr(AC.m*D.x) + 2*AC.b*D.x - 2*AC.m*D.x*B.y + sqr(AC.b) - 2*AC.b*B.y + sqr(B.y) == 4*r*r
    7. Rewritten into the neater
      (sqr(D.x) + sqr(AC.m*D.x)) + ( 2*D.x + 2*AC.b*D.x - 2*AC.m*B.y*D.x ) + ( - sqr(B.x) + sqr(AC.b) - 2*AC.b*B.y + sqr(B.y) ) == 4*r*r which can be refactored to
      (1 + sqr(AC.m)) * sqr(D.x) + 2*( 1 + AC.b - AC.m*B.y ) * D.x + ( sqr(B.y) - sqr(B.x) + sqr(AC.b) - 2*AC.b*B.y - 4*r*r ) == 0
    8. This now fits nicely into the quadratic formula (ie x == (-bb +- sqrt( sqr(bb) - 4*aa*cc ) / 2*aa) (using aa to avoid confusion with earlier variables), with aa := 1 + sqr(AC.m);, bb := 2*( 1 + AC.b - AC.m*B.y );, and cc := sqr(B.y) - sqr(B.x) + sqr(AC.b) - 2*AC.b*B.y - sqr(A.r+B.r);.
    9. Now we might get two solution so lets save the parts using -bb/2aa +- sqrt(sqr(bb)-4*aa*cc)/2*aa: first_term := -bb/(2*a); and second_term := sqrt(sqr(bb)-4*aa*cc)/2*aa;.
    10. First solution D1.x = first_term + second_term; with D1.y = AC.m * D1.x + AC.b, and a second solution D2.x = first_term + second_term; with D2.y = AC.m * D2.x + AC.b.
    11. Find the distances to A: D1.l := sqrt( sqr(D1.x-A.x) + sqr(D1.y-A.y) ); and D2.l = sqrt( sqr(D2.x-A.x) + sqr(D2.y-A.y) ); (actually it is more efficient to skip both square roots, but it makes no difference).
    12. The closer one is one you want D := D1 if D1.l < D2. l else D2;.
    1. The midpoint of DB, lets call it E, is the collision (I don't know how this generalises if the radii aren't equal).
    2. So constuct the line DB.m := (B.y-D.y)/(B.x-D.x); and DB.b = B.y - DB.m*B.x;.
    3. We don't need the length to determine the length since it should be BD.l == A.r + B.r, so sqrt( sqr(E.x-B.x) + sqr(E.y-B.y) ) == B.r.
    4. Again, we can substitute for E because we know it's on BD so E.y == BD.m * E.x + BD.b, getting sqrt( sqr(E.x-B.x) + sqr(BD.m * E.x + BD.b-B.y) ) == B.r.
    5. Expanding into sqr(E.x) - 2*E.x*B.x + sqr(B.x) + sqr(BD.m)*sqr(E.x) + 2*BD.m*E.x*BD.b - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) == sqr(B.r)
    6. Which collects into

    sqr(E.x) + sqr(BD.m)*sqr(E.x) + 2*BD.m*E.x*BD.b - 2*E.x*B.x + sqr(B.x) - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) == sqr(B.r)
    (1 + sqr(BD.m)) * sqr(E.x) + 2*(BD.m*BD.b - B.x) * E.x + sqr(B.x) + sqr(B.y) - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) - sqr(B.r) == 0
    aa := (1 + sqr(BD.m)); bb := 2*(BD.m*BD.b - B.x); cc := sqr(B.y) - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) - sqr(B.r);, quadratic formula, and then get your two points and choose the one closer to B.

I wish I was just whoring for karma or the necromancer badge, but I really needed to work this out and figured I'd share. Ugh, I think I need to lay down now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文