XNA-如何计算二维圆相互碰撞后的速度?

XNA- How do I calculate the velocities of 2D circles after they collided with each other?

我一直在研究让圆圈相互碰撞,这是我到目前为止想出的:

foreach(Circle circle in circles)
    foreach(Circle circle2 in circles)
            {
                bool HasCollided = false;
                if(circle.ID != circle2.ID)
                {
                    double squareRoot =(Math.Pow(((int)circle.position.X - (int)circle2.position.X), 2) + Math.Pow((int)circle.position.Y - (int)circle2.position.Y, 2));
                    double distanceBetweenCentres = Math.Sqrt(squareRoot);
                    if(distanceBetweenCentres < (circle.radius + circle2.radius))HasCollided = true;                           
                    else if(distanceBetweenCentres> (circle.radius + circle2.radius)) HasCollided = false;
                    if(HasCollided == true)
                    {
                        double newVelX1 = ((circle.velocity.X * (circle.Mass - circle2.Mass) + (2 * circle2.Mass * circle2.velocity.X)) / (circle.Mass + circle2.Mass));
                        double newVelY1 = ((circle.velocity.Y * (circle.Mass - circle2.Mass) + (2 * circle2.Mass * circle2.velocity.Y)) / (circle.Mass + circle2.Mass));

                        double newVelX2 = ((circle2.velocity.X * (circle2.Mass - circle.Mass) + (2 * circle.Mass * circle.velocity.Y)) / (circle2.Mass + circle.Mass));
                        double newVelY2 = ((circle2.velocity.Y * (circle2.Mass - circle.Mass) + (2 * circle.Mass * circle.velocity.Y)) / (circle2.Mass + circle.Mass));

                        circle.velocity = new Vector2((int)newVelX1, (int)newVelY1);
                        circle2.velocity = new Vector2((int)newVelX2, (int)newVelY2);
                    }
                }
            }
        }

代码在圆圈碰撞时确实有效,但是最终速度的方程式似乎使圆圈要么在屏幕上跳跃,要么慢慢地相互掉落,我尝试在网上找到解决方案,但我找不到'无法正常工作。关于如何修复它有什么建议吗?

新代码和 GIF 来展示它的作用:

 if(circle.ID < circle2.ID)
                {
                    double squareRoot =(Math.Pow(((int)circle.position.X - (int)circle2.position.X), 2) + Math.Pow((int)circle.position.Y - (int)circle2.position.Y, 2));
                    double distanceBetweenCentres = Math.Sqrt(squareRoot);
                    if(distanceBetweenCentres < (circle.radius + circle2.radius))HasCollided = true;                           
                    else if(distanceBetweenCentres> (circle.radius + circle2.radius)) HasCollided = false;
                    if(HasCollided == true)
                    {
                        double newVelX1 = ((circle.velocity.X * (circle.Mass - circle2.Mass) + (2 * circle2.Mass * circle2.velocity.X)) / (circle.Mass + circle2.Mass));
                        double newVelY1 = ((circle.velocity.Y * (circle.Mass - circle2.Mass) + (2 * circle2.Mass * circle2.velocity.Y)) / (circle.Mass + circle2.Mass));

                        double newVelX2 = ((circle2.velocity.X * (circle2.Mass - circle.Mass) + (2 * circle.Mass * circle.velocity.Y)) / (circle2.Mass + circle.Mass));
                        double newVelY2 = ((circle2.velocity.Y * (circle2.Mass - circle.Mass) + (2 * circle.Mass * circle.velocity.Y)) / (circle2.Mass + circle.Mass));

                        circle.velocity = new Vector2((float)newVelX1, (float)newVelY1);
                        circle2.velocity = new Vector2((float)newVelX2, (float)newVelY2);
                    }
                }

如果他们跳来跳去,很可能是因为在您进行下一次碰撞检查时他们还没有分开。尝试将圆从撞击点移动半径。

以第一个圆为例:

Cp = Cp + Normalize(Ip - Cp) * Cr

其中 Ip 是冲击位置,Cp 是圆位置,Cr 是圆半径。 如果它似乎将圆圈进一步推向碰撞,请在归一化之前交换 Ip 和 Cp。

编辑:在运行我自己的代码之后想出了这个:

问题 1: 在 newVelX2 中,您正在比较 CircleVelocity.Y 而不是 .X

问题二: 您没有检查以前评估过的对。我用下面的代码解决了这个问题: `

for (int c1Index = 0; c1Index < RigidBodies.Count; c1Index++)
 {
      for (int c2Index = c1Index + 1; c2Index < RigidBodies.Count; c2Index++)
      {
           HandleCollisions(RigidBodies[c1Index], RigidBodies[c2Index]);
      }
 }

This prevents you from evaluating a pair twice. What I mean is, if following your code with 3 circles in the list:

Outer Loop = Circle0
Inner Loop = Circle0 <-- C0 is skipped
Inner Loop = Circle1 <-- C0 and C1 pair evaluated
Inner Loop = Circle2 <-- C0 and C2 pair evaluated
Outer Loop = Circle1 
Inner Loop = Circle0 <-- C1 and C0 pair evaluated *PROBLEM: We already did this pair*
Inner Loop = Circle1 <-- C1 is skipped
Inner Loop = Circle2 <-- C1 and C2 pair evaluated
Outer Loop = Circle2 
Inner Loop = Circle0 <-- C2 and C0 pair evaluated *PROBLEM: We already did this pair*
Inner Loop = Circle1 <-- C2 and C1 pair evaluated *PROBLEM: We already did this pair*
Inner Loop = Circle2 <-- C2 is skipped

`

我的代码解决了这个问题,方法是让内部循环从当前评估的循环之后的一个开始,因为它之前的任何事情都已经处理过了。另一种选择是维护一个已评估对的列表,这显然会占用更多内存,但可以完成工作。

对于我的演示,我不必先将圆圈分开。但是,如果您在彼此内部生成圆圈,它们就会像那样卡住。因此,如果您预计圈子有时会粘在一起,您可能希望保留我原始答案中的伪代码。

此外,下面的代码显示了我改编自 this link 的更准确的响应机制:

 private void HandleCollisions(Circle Circle1, Circle Circle2)
    {
        if (!HaveCollided(Circle1, Circle2))
            return;

        Vector2D distance = Circle1.Position - Circle2.Position;
        Vector2D normalVector = distance.Normalize();

        Vector2D tangentVector = normalVector.GetTangentVector();

        float c1VelocityAlongNormal = VectorMath.CalculateDotProduct(normalVector, Circle1.Velocity);
        float c2VelocityAlongNormal = VectorMath.CalculateDotProduct(normalVector, Circle2.Velocity);

        float c1VelocityAlongTan = VectorMath.CalculateDotProduct(tangentVector, Circle1.Velocity);
        float c2VelocityAlongTan = VectorMath.CalculateDotProduct(tangentVector, Circle2.Velocity);

        float massSum = Circle1.Mass + Circle2.Mass;

        float c1Speed = (c1VelocityAlongNormal * (Circle1.Mass - Circle2.Mass) + (2.0f * Circle2.Mass * c2VelocityAlongNormal) / massSum);
        float c2Speed = (c2VelocityAlongNormal * (Circle1.Mass - Circle2.Mass) + (2.0f * Circle1.Mass * c1VelocityAlongNormal) / massSum);

        Vector2D c1FinalVelocity = normalVector * c1Speed;
        Vector2D c2FinalVelocity = normalVector * c2Speed;

        Vector2D c1Tangent = tangentVector * c1VelocityAlongTan;
        Vector2D c2Tangent = tangentVector * c2VelocityAlongTan;

        Circle1.Velocity = c1FinalVelocity + c1Tangent;
        Circle2.Velocity = c2FinalVelocity + c2Tangent;
    }

    **FROM VECTOR2D CLASS**
    class Vector2D
{
    public float X, Y;

    public Vector2D()
    {
        this.X = 0.0f;
        this.Y = 0.0f;
    }

    public Vector2D(float X, float Y)
    {
        this.X = X;
        this.Y = Y;
    }

    public Vector2D(Vector2D vectorToCopy)
    {
        this.X = vectorToCopy.X;
        this.Y = vectorToCopy.Y;
    }

    public float CalculateMagnitudeSquared()
    {
        return (this.X * this.X) + (this.Y * this.Y);
    }

    public float CalculateMagnitude()
    {
        return (float)Math.Sqrt((this.X * this.X) + (this.Y * this.Y));
    }

    public Vector2D GetTangentVector()
    {
        return new Vector2D(-this.Y, this.X).Normalize();
    }

    public Vector2D Normalize()
    {
        return new Vector2D(this / CalculateMagnitude());
    }

    public static Vector2D operator+(Vector2D thisVector, Vector2D otherVector)
    {
        return new Vector2D(thisVector.X + otherVector.X, thisVector.Y + otherVector.Y);
    }

    public static Vector2D operator-(Vector2D thisVector, Vector2D otherVector)
    {
        return new Vector2D(thisVector.X - otherVector.X, thisVector.Y - otherVector.Y);
    }

    public static Vector2D operator*(Vector2D thisVector, Vector2D otherVector)
    {
        return new Vector2D(thisVector.X * otherVector.X, thisVector.Y * otherVector.Y);
    }

    public static Vector2D operator*(Vector2D thisVector, float scalar)
    {
        return new Vector2D(thisVector.X * scalar, thisVector.Y * scalar);
    }

    public static Vector2D operator/(Vector2D thisVector, Vector2D otherVector)
    {
        return new Vector2D(thisVector.X / otherVector.X, thisVector.Y / otherVector.Y);
    }

    public static Vector2D operator/(Vector2D thisVector, float scalar)
    {
        return new Vector2D(thisVector.X / scalar, thisVector.Y / scalar);
    }
}

这不是最简洁的代码...只是将它放在一起来测试这些东西。

任何进一步的问题,让我知道