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

标签 c# xna 2d collision geometry

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

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

New gif after changing code again

最佳答案

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

以第一个圆为例:

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

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

编辑:自己运行代码后想出这个:

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

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

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

这可以防止您对一对进行两次评估。我的意思是,如果按照您的代码,列表中有 3 个圆圈:

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

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

还有什么问题可以私信我

关于c# - XNA-如何计算二维圆相互碰撞后的速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36023170/

相关文章:

java - 将成绩转换为 GPA(带小数)

c# - 十进制值和可选字符串的正则表达式

c# - 无法在 XNA 中隐式转换类型

c# - 如何在 EF Core 中重命名一对多关系中的列?

graphics - HLSL 点光源着色器导致图 block 单独着色

c# - 游戏滞后(初学者)

c# - Unity 在没有 IsTrigger 的情况下使用 OnCollisionEnter2D 检测碰撞

python - 如何将具有对象 dtype 的 Numpy 二维数组转换为常规的二维 float 组

c# - 如何将自定义类型的列表/数组写入 HDF5 文件?

c# - 根据两个条件对基于字符串的数组进行排序