34

我将开发一个 2-d 球类游戏,其中两个球(圆)发生碰撞。现在我在确定碰撞点时遇到了问题(实际上,确定它们是否在 x 轴/y 轴上发生碰撞)。我有一个想法,当 2 个球的 y 坐标之间的差异大于 x 坐标差异时,它们会在它们的 y 轴上发生碰撞,否则它们会在它们的 x 轴上发生碰撞。我的想法正确吗?我在我的游戏中实现了这个东西。通常它工作得很好,但有时它会失败。谁能告诉我我的想法是否正确?如果不是,那么为什么,还有更好的方法吗?

x轴上的碰撞是指圆的第1、4、5或8个八分圆,y轴是指圆的第2、3、6或7个八分圆。

提前致谢!

4

6 回答 6

123

圆圈之间的碰撞很容易。想象有两个圆圈:

  • C1,中心为 (x1,y1),半径为 r1;
  • C2 具有中心 (x2,y2) 和半径 r2。

想象在这两个中心点之间有一条线。根据定义,从中心点到任一圆的边缘的距离等于它们各自的半径。所以:

  • 如果圆的边缘相接触,则圆心之间的距离为 r1+r2;
  • 任何更大的距离,圆圈不会接触或碰撞;和
  • 少一点,然后碰撞。

因此,如果出现以下情况,您可以检测到碰撞:

(x2-x1)^2 + (y2-y1)^2 <= (r1+r2)^2

意味着中心点之间的距离小于半径之和。

相同的原理可以应用于检测三个维度的球体之间的碰撞。

编辑:如果你想计算碰撞点,一些基本的三角学可以做到这一点。你有一个三角形:

        (x1,y1)
        |\
        | \
        |  \ sqrt((x2-x1)^2 + (y2-y1)^2) = r1+r2
|y2-y1| |   \
        |    \
        |   X \
(x1,y2) +------+ (x2,y2)
         |x2-x1|

表达式|x2-x1||y2-y1|是绝对值。所以对于角度 X:

        |y2 - y1|
sin X =  -------
         r1 + r2

        |x2 - x1|
cos X =  -------
         r1 + r2

        |y2 - y1|
tan X =  -------
        |x2 - x1|

获得角度后,您可以通过将它们应用于新三角形来计算交点:

  +
  |\
  | \
b |  \ r2
  |   \
  |  X \
  +-----+
     a

在哪里:

        a
cos X = --
        r2

所以

a = r2 cos X

从前面的公式:

       |x2 - x1|
a = r2 -------
        r1 + r2

一旦有了 a 和 b,您就可以根据 (x2,y2) 偏移量 (a,b) 来计算碰撞点。您甚至不需要为此计算任何正弦、余弦或反正弦或余弦。或任何平方根。所以速度很快。

但是,如果您不需要精确的角度或碰撞点,而只想要八分圆,您可以通过了解有关切线的信息来进一步优化它,即:

  • 0 <= tan X <= 1 表示 0 <= X <= 45 度;
  • tan X >= 1 表示 45 <= X <= 90
  • 0 >= 棕褐色 X >= -1 对于 0 >= X => -45;
  • tan X <= -1 表示 -45 >= X => -90;和
  • 棕褐色 X = 棕褐色 (X+180) = 棕褐色 (X-180)。

这四个度数范围对应于圆的四个八分圆。其他四个偏移 180 度。如上所示,正切可以简单地计算为:

        |y2 - y1|
tan X =  -------
        |x2 - x1|

失去绝对值,这个比率会告诉你碰撞在四个八分圆中的哪一个(通过上述切线​​范围)。要计算出确切的八分圆,只需比较 x1 和 x2 以确定哪个是最左边的。

另一个单的碰撞八分圆是偏移的(C1 上的八分圆 1 表示 C2 上的八分圆 5、2 和 6、3 和 7、4 和 8 等)。

于 2009-11-15T06:02:37.433 回答
10

正如 cletus 所说,您想使用两个球的半径之和。您想计算球中心之间的总距离,如下所示:

Ball 1:  center: p1=(x1,y1)  radius: r1
Ball 2:  center: p2=(x2,y2)  radius: r2

collision distance: R= r1 + r2
actual distance:    r12= sqrt( (x2-x1)^2 + (y2-y1)^2 )

只要 (r12 < R) 就会发生碰撞。正如 Artelius 所说,它们实际上不应该在 x/y 轴上碰撞,它们以特定角度碰撞。除了,您实际上并不想要那个角度;你想要碰撞向量。这是两个圆在碰撞时的中心之间的差异:

collision vector: d12= (x2-x1,y2-y1) = (dx,dy)
actual distance:  r12= sqrt( dx*dx + dy*dy )

请注意,在计算实际距离时,您已经在上面计算了 dx 和 dy,因此您不妨跟踪它们以用于此类目的。您可以使用此碰撞矢量来确定球的新速度——您最终将通过一些因素缩放碰撞矢量,并将其添加到旧速度......但是,要回到实际的碰撞观点:

collision point:  pcollision= ( (x1*r2+x2*r1)/(r1+r2), (y1*r2+y2*r1)/(r1+r2) )

要弄清楚如何找到球的新速度(通常是为了更了解整个情况),您可能应该找到一本高中物理书或同等书籍。不幸的是,我不知道一个好的网络教程——建议,有人吗?

哦,如果仍然想坚持 x/y 轴的事情,我认为你已经做对了:

if( abs(dx) > abs(dy) ) then { x-axis } else { y-axis }

至于它为什么会失败,如果没有更多信息,很难说清楚,但你可能会遇到问题,因为你的球移动得太快,并且在一个时间步内相互通过。有一些方法可以解决这个问题,但最简单的方法是确保它们不会移动得太快......

于 2009-11-15T06:48:44.210 回答
8

该站点解释了物理原理推导出算法,并提供了 2D 球碰撞的代码。

在此函数计算以下内容后计算八分圆:碰撞点相对于物体 a 质心的位置;碰撞点相对于物体质心a的位置

/**
This function calulates the velocities after a 2D collision vaf, vbf, waf and wbf from information about the colliding bodies
@param double e coefficient of restitution which depends on the nature of the two colliding materials
@param double ma total mass of body a
@param double mb total mass of body b
@param double Ia inertia for body a.
@param double Ib inertia for body b.
@param vector ra position of collision point relative to centre of mass of body a in absolute coordinates (if this is
                 known in local body coordinates it must be converted before this is called).
@param vector rb position of collision point relative to centre of mass of body b in absolute coordinates (if this is
                 known in local body coordinates it must be converted before this is called).
@param vector n normal to collision point, the line along which the impulse acts.
@param vector vai initial velocity of centre of mass on object a
@param vector vbi initial velocity of centre of mass on object b
@param vector wai initial angular velocity of object a
@param vector wbi initial angular velocity of object b
@param vector vaf final velocity of centre of mass on object a
@param vector vbf final velocity of centre of mass on object a
@param vector waf final angular velocity of object a
@param vector wbf final angular velocity of object b
*/
CollisionResponce(double e,double ma,double mb,matrix Ia,matrix Ib,vector ra,vector rb,vector n,
    vector vai, vector vbi, vector wai, vector wbi, vector vaf, vector vbf, vector waf, vector wbf) {
  double k=1/(ma*ma)+ 2/(ma*mb) +1/(mb*mb) - ra.x*ra.x/(ma*Ia) - rb.x*rb.x/(ma*Ib)  - ra.y*ra.y/(ma*Ia)
    - ra.y*ra.y/(mb*Ia) - ra.x*ra.x/(mb*Ia) - rb.x*rb.x/(mb*Ib) - rb.y*rb.y/(ma*Ib)
    - rb.y*rb.y/(mb*Ib) + ra.y*ra.y*rb.x*rb.x/(Ia*Ib) + ra.x*ra.x*rb.y*rb.y/(Ia*Ib) - 2*ra.x*ra.y*rb.x*rb.y/(Ia*Ib);
  double Jx = (e+1)/k * (Vai.x - Vbi.x)( 1/ma - ra.x*ra.x/Ia + 1/mb - rb.x*rb.x/Ib)
     - (e+1)/k * (Vai.y - Vbi.y) (ra.x*ra.y / Ia + rb.x*rb.y / Ib);
  double Jy = - (e+1)/k * (Vai.x - Vbi.x) (ra.x*ra.y / Ia + rb.x*rb.y / Ib)
     + (e+1)/k  * (Vai.y - Vbi.y) ( 1/ma - ra.y*ra.y/Ia + 1/mb - rb.y*rb.y/Ib);
  Vaf.x = Vai.x - Jx/Ma;
  Vaf.y = Vai.y - Jy/Ma;
  Vbf.x = Vbi.x - Jx/Mb;
  Vbf.y = Vbi.y - Jy/Mb;
  waf.x = wai.x - (Jx*ra.y - Jy*ra.x) /Ia;
  waf.y = wai.y - (Jx*ra.y - Jy*ra.x) /Ia;
  wbf.x = wbi.x - (Jx*rb.y - Jy*rb.x) /Ib;
  wbf.y = wbi.y - (Jx*rb.y - Jy*rb.x) /Ib;
}
于 2009-11-15T07:42:00.297 回答
2

我同意提供的答案,它们非常好。
我只想告诉你一个小陷阱:如果球的速度很高,你可能会错过碰撞,因为在给定的步骤中,圆永远不会相交。
解决方法是求解运动方程并找到正确的碰撞时刻。

无论如何,如果您实施您的解决方案(X 轴和 Y 轴上的比较),您将获得好的旧乒乓球!http://en.wikipedia.org/wiki/Pong
:)

于 2009-11-17T13:57:17.150 回答
1

它们碰撞的点在两个圆的中点之间的线上,它与任一中点的距离就是相应圆的半径。

于 2009-11-15T07:06:46.687 回答
0

更直接地回答您的问题:是的,根据您制定的规则和要求,如果 Y 的差异大于球接触时 X 的差异,那么这些球会在 Y 轴上发生碰撞。

如果这是您正在实施的,那么您将得到“X 轴或 Y 轴碰撞?”问题的正确答案。但我认为你在这里得到这么多答案而你似乎无法利用的原因是

  • 你在问错误的问题(不是在这里 - 在你的程序中);或者

  • 你没有正确使用答案。

我敢肯定,我们很多人都编写了弹跳球程序,而且我怀疑我们中没有人尝试过基于八分圆和轴来模拟碰撞。所以我怀疑要么你有一个非常原始的新方法,要么你只是做错了。因此,我建议回去检查你的方法和假设。

于 2009-11-15T08:18:59.833 回答