【问题标题】:circle-circle collision圈圈碰撞
【发布时间】:2021-12-27 07:21:49
【问题描述】:

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

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

提前致谢!

【问题讨论】:

    标签: algorithm collision-detection


    【解决方案1】:

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

    • 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 表示 45
    • 0 >= tan X >= -1 for 0 >= X => -45;
    • tan X = 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等)。

    【讨论】:

    • 作为一个额外的速度技巧,您可以使用毕达哥拉斯定理计算距离,但不要计算昂贵的平方根,只需保持您的总中心到中心距离并与半径之和。
    • 我知道。那不是问题。我的问题是确定
      碰撞点,而不是他们碰撞的时间。我的问题是找出他们的碰撞轴,其中 x 轴表示圆的 1,第 4,5,8 八分圆。 y 轴表示圆的第 2、3、6、7 八分圆。
    • 请注意,您实际上不需要任何三角函数来找到碰撞点。在这种物理/几何问题中,通常有一个使用向量数学的解决方案,它比转换为角度然后再转换回来更简单、更快、更稳健。
    • +1 表示在发现 stackoverflow 不进行 LaTeX 渲染后没有放弃。
    • @corningstorm:你会注意到我没有转换成角度或角度。我只是在接下来的计算中使用了该术语。
    【解决方案2】:

    正如 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

    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 }
    

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

    【讨论】:

    • Re GMan 对 cletus 回答的评论:请注意,如果距离 r12 正好等于碰撞距离 R,则上面的“碰撞点”只是一个精确的碰撞点。但是,该点的公式是稳健:如果两个点比 R 更近(或者更远,就此而言),“碰撞点”会根据实际距离 r12 进行缩放。
    【解决方案3】:

    本站explains the physicsderives the algorithm,并提供code for collisions的二维球。

    在此函数计算以下内容后计算八分圆:碰撞点相对于物体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;
    }
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 或者,您可以扫描球体(形成胶囊),然后找出它们重叠的位置。
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

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

        • 您问错了问题(不在此处 - 在您的程序中);或

        • 你没有正确使用答案。

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

        【讨论】:

          猜你喜欢
          • 2013-03-05
          • 1970-01-01
          • 2015-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多