c# - 使用 XNA/MonoGame 的不同大小和质量的正确球体碰撞解决方案

标签 c# xna physics monogame

我目前正在使用以下方法来计算两个相互弹回的球体。这是在使用 3D 对象的 2D 乒乓球游戏中使用的(试图让我的头脑围绕 3D)。大多数情况下一切正常,但有时(通常当 X 或 Y 速度沿相同方向移动时,只是一个比另一个快)物理会做一些奇怪的事情。

返回的 float 只是我用来改变球碰撞时播放的声音的质量差异。 谁能看出我的计算有任何错误:

internal float ResolveCollision(Ball otherBall)
{
    if (otherBall == this) { return 0f; }
    if (this.GetBoundingSphere().Intersects(otherBall.GetBoundingSphere()))
    {
        // Attempt to step the balls back so they are just barely touching
        Vector3 dd = Position - otherBall.Position;
        dd.Normalize();
        Position += dd / 2;
        otherBall.Position -= dd / 2;

        ///http://williamecraver.wix.com/elastic-equations

        Vector3 V1 = Velocity;
        Vector3 P1 = Position;
        float M1 = Mass;
        float A1 = getMovementAngle(V1.X, V1.Y);

        Vector3 V2 = otherBall.Velocity;
        Vector3 P2 = otherBall.Position;
        float M2 = otherBall.Mass;
        float A2 = getMovementAngle(V2.X, V2.Y);

        float CA = getContactAngle(P1, P2);

        // Recalculate x and y components based of a rotated axis, having the x axis parallel to the contact angle.
        Vector3 V1XR = V1 * (float)Math.Cos(A1 - CA);
        Vector3 V1YR = V1 * (float)Math.Sin(A1 - CA);

        Vector3 V2XR = V2 * (float)Math.Cos(A2 - CA);
        Vector3 V2YR = V2 * (float)Math.Sin(A2 - CA);

        //Now solve the x components of the velocity as if they were in one dimension using the equation;
        Vector3 V1f = (V1 * (M1 - M2) + 2 * M2 * V2) / (M1 + M2);
        Vector3 V2f = (V2 * (M2 - M1) + 2 * M1 * V1) / (M1 + M2);

        Vector3 V1fXR = (V1 * (float)Math.Cos(A1 - CA) * (M1 - M2) + 2 * M2 * V2 * (float)Math.Cos(A2 - CA)) / (M1 + M2);
        Vector3 V2fXR = (V2 * (float)Math.Cos(A2 - CA) * (M2 - M1) + 2 * M1 * V1 * (float)Math.Cos(A1 - CA)) / (M1 + M2);

        //Now find the x and y values for the un-rotated axis by equating for the values when the axis are rotated back.
        Vector3 V1fX = V1fXR * (float)Math.Cos(CA) + V1YR * (float)Math.Cos(CA + MathHelper.PiOver2);
        Vector3 V1fY = V1fXR * (float)Math.Sin(CA) + V1YR * (float)Math.Sin(CA + MathHelper.PiOver2);
        Vector3 V2fX = V2fXR * (float)Math.Cos(CA) + V2YR * (float)Math.Cos(CA + MathHelper.PiOver2);
        Vector3 V2fY = V2fXR * (float)Math.Sin(CA) + V2YR * (float)Math.Sin(CA + MathHelper.PiOver2);

        // Add it all up
        Vector3 nV1 = V1fX + V1fY;
        Vector3 nV2 = V2fX + V2fY;

        ///////////////////////////////////////////
        // Correct Velocity & Move apart
        //////////////////////////////////////////
        Velocity = v3check(nV1, MAXSPEED, -MAXSPEED);
        otherBall.Velocity = v3check(nV2, MAXSPEED, -MAXSPEED);

        // Step the balls forward (by there Velocity) just a bit so they are no longer touching
        Position += Velocity * _lastDT * .25f;
        otherBall.Position += otherBall.Velocity * otherBall._lastDT * .25f;

        return BMDMath.toFloat(Mass - otherBall.Mass);
    }

    return 0f;
}

我有以下辅助方法来转换一些角度(这可能是问题所在:

private static float getMovementAngle(double vx, double vy)
{
    return MathHelper.ToDegrees((float)Math.Atan2(vy, vx));
}


private static float getContactAngle(Vector3 o1, Vector3 o2)
{
    Vector3 d = o1 - o2;
    return MathHelper.ToDegrees((float)Math.Atan2(d.Y, d.X));
}

最佳答案

应尽可能避免使用角度。事实上,用角度计算碰撞是很糟糕的。有这么多精彩的矢量数学可以帮助您进行计算。

那么让我们从计算碰撞平面开始吧。实际上,我们不需要整个平面,只需要它的法线。在两个球体的情况下,这只是连接两个中心的矢量:

collision

var collisionNormal = Position - otherBall.Position;
collisionNormal.Normalize();
//The direction of the collision plane, perpendicular to the normal
var collisionDirection = new Vector3(-collisionNormal.Y, collisionNormal.X, 0);

现在将两个速度分成两部分。一部分平行于法线,另一部分垂直。那是因为垂直部分不受碰撞影响。 V2 的拆分如下所示:

collision

var v1Parallel = Vector3.Dot(collisionNormal, V1) * collisionNormal;
var v1Ortho    = Vector3.Dot(collisionDirection, V1) * collisionDirection;
var v2Parallel = Vector3.Dot(collisionNormal, V2) * collisionNormal;
var v2Ortho    = Vector3.Dot(collisionDirection, V2) * collisionDirection;

我们可以通过添加其组件来重建原始速度:

v1 = v1Parallel + v1Ortho;
v2 = v2Parallel + v2Ortho;

如前所述,正交分​​量不受碰撞影响。现在我们可以对并联组件应用一些物理学:

var v1Length = v1Parallel.Length;
var v2Length = v2Parallel.Length;
var commonVelocity = 2 * (this.Mass * v1Length + otherBall.Mass * v2Length) / (this.Mass + otherBall.Mass);
var v1LengthAfterCollision = commonVelocity - v1Length;
var v2LengthAfterCollision = commonVelocity - v2Length;
v1Parallel = v1Parallel * (v1LengthAfterCollision / v1Length);
v2Parallel = v2Parallel * (v2LengthAfterCollision / v2Length);

现在我们可以重新组合组件并且:

this.Velocity = v1Parallel + v1Ortho;
otherBall.Velocity = v2Parallel + v2Ortho;

关于c# - 使用 XNA/MonoGame 的不同大小和质量的正确球体碰撞解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19189322/

相关文章:

c# - IIS WCF 服务身份验证、javascript 客户端

c# - 如何在 ASP.NET 中显示 "View"表(SQL 表)?

c# - 在紫色屏幕中更改 RenderTarget 结果?

javascript - 如何确定一个点当前位于椭圆路径的哪一半?

Python:使用生物萨伐尔定律计算电线的磁场

C# 相当于 Java `<F extends FieldType<?>> void add(F field)`

c# - 编辑视频文件的元数据

c# - Xbox编程

c# - Unity C# : Caching keyboard/controller state?

javascript - 模拟球滚下斜坡 - HTML5 Canvas JavaScript