Math 计算两个移动球将碰撞的x/y点

Math 计算两个移动球将碰撞的x/y点,math,language-agnostic,collision-detection,physics,Math,Language Agnostic,Collision Detection,Physics,我正在尝试(本质上)做一个简单的台球游戏,并且希望能够预测一个球一旦击中另一个球,它会去哪里 我认为,第一部分是计算主球是否会击中任何物体,如果击中了,会在何处碰撞。我可以算出一条线和一个球的碰撞点,但不能算出两个球的碰撞点 给定两个球的x/y位置和速度,我如何计算它们碰撞的点 (附言:我知道我可以通过计算每一步两个球之间的距离来做到这一点,但我希望得到更优雅、更优化的东西。) 设置示例:尝试计算红点 需要注意的一些事情: 当两个球的半径r相撞时,它们的中心相距2r 可以假设第一个球沿直线运动

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

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

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

(附言:我知道我可以通过计算每一步两个球之间的距离来做到这一点,但我希望得到更优雅、更优化的东西。)

设置示例:尝试计算红点


需要注意的一些事情:

  • 当两个球的半径
    r
    相撞时,它们的中心相距
    2r
  • 可以假设第一个球沿直线运动(好吧,第一个近似值,但从这里开始),并且可以找到此路径与从第一个球到第二个球的方向之间的角度,
    alpha
  • 你知道静止球的中心,不是吗
现在你有一些几何要做

请执行以下操作:

  • 将第一个(移动)球的当前中心标记为点
    A
  • 将固定球的中心标记为点
    B
  • 构造线段
    AB
  • A
    沿移动方向构造光线
    R
  • 围绕
    B
    构建一个半径为
    2r
    的圆
  • 从垂直于
    R
    B
    放置一个线段,调用交点
    C
  • 你知道距离
    AB
    ,你可以找到
    AB
    R
    之间的角度
    alpha
    ,用正弦定律找到
    BC
    的长度
  • 根据该长度确定是否存在0、1或2个解决方案。如果有0或1,则完成
  • 在圆与A相交的地方构造点
    D
    ,然后再次使用正弦定律计算距离AD
  • 碰撞点是
    BD
  • 现在你什么都知道了

    从中构造高效的代码只是一个练习


    顺便说一句——如果两个球都在移动,这种构造将不起作用,但你可以将其转换为一个静止的帧,以这种方式求解,然后再转换回来。只需确保在反向转换后检查溶液是否在允许的区域内


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

    画出@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”替换为正确版本等)

  • A.x和A.y是x&y位置,A.r是A的半径,A.v是速度,A.v.x是它的x分量,A.v.y是它的y分量
  • B是相同的,但没有速度(或者更准确地说,从A中减去B的速度,因此B相对来说是静止的)
  • 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
    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)
    然后只需插入
    C.x
    即可获得
    C.y:=AB.m*C.x+AB.b
  • 你可以忽略正弦定律,因为我们有AB和BC,所以我们可以使用毕达哥拉斯定理得到
    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
    so
    D.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;