如何解决与不可移动物体的二维碰撞?

3
我正在创建一个二维物理引擎,但在可移动和不可移动物体之间的某种类型的碰撞中遇到了问题。所谓可移动是指(x,y)值可以改变,而不是参考框架可以或不能改变。
例如,球撞击墙壁就是可移动物体与不可移动物体碰撞的一种情况。

ball and wall collision

我相信在这种情况下需要使用类似于法向力的东西,但我不确定如何用它来找到碰撞的结果。

以下是我目前拥有的代码。这段代码用于两个移动实体之间的碰撞,但我需要添加一个当其中一个实体不移动时的情况:

private static void UpdateEntities(PhysicsEntity a, PhysicsEntity b)
    {
        var collisionAngle = Math.Atan2(a.Position.Y - b.Position.Y, a.Position.X - b.Position.X);
        var angleA = a.Velocity.Direction - collisionAngle;
        var angleB = b.Velocity.Direction - collisionAngle;

        var vAx = a.Velocity.Magnitude * Math.Cos(angleA);
        var vAy = a.Velocity.Magnitude * Math.Sin(angleA);
        var vBx = b.Velocity.Magnitude * Math.Cos(angleB);
        var vBy = b.Velocity.Magnitude * Math.Sin(angleB);

        var vfAx = ((vAx * (a.Mass - b.Mass) + 2 * b.Mass * vBx) / (a.Mass + b.Mass)) * a.Material.Elasticity;
        var vfBx = ((vBx * (b.Mass - a.Mass) + 2 * a.Mass * vAx) / (a.Mass + b.Mass)) * b.Material.Elasticity;
        var vfAy = vAy * a.Material.Elasticity;
        var vfBy = vBy * b.Material.Elasticity;

        var magA = Math.Sqrt(Math.Pow(vfAx, 2) + Math.Pow(vfAy, 2));
        var magB = Math.Sqrt(Math.Pow(vfBx, 2) + Math.Pow(vfBy, 2));
        var dirA = Math.Atan2(vfAy, vfAx) + collisionAngle;
        var dirB = Math.Atan2(vfBy, vfBx) + collisionAngle;

        a.Velocity.X = magA * Math.Cos(dirA);
        a.Velocity.Y = magA * Math.Sin(dirA);
        b.Velocity.X = magB * Math.Cos(dirB);
        b.Velocity.Y = magB * Math.Sin(dirB);

    }

我尝试将不可移动物体的速度设置为可移动物体速度的相反方向,但这导致物体相互穿透。


我不这么认为。不应该是不动的物体在相反方向施加同样的力吗?(我不确定你的问题是要澄清还是想引导我得出答案) - Jedi_Maseter_Sam
“不应该?”你有两个实体,ab。它们中哪一个是不可移动的,为什么要设置它的速度? - Beta
ab的代码适用于所有碰撞。它们都不必是不可移动的,但我需要添加一个当其中一个不可移动时的情况。已更新问题以更具体。 - Jedi_Maseter_Sam
1
这段代码一般来说可以运行吗?如果可以的话,你可以通过将物体的质量设置得非常高来使其不可移动或不可推动。 - Beta
代码是可以工作的。将质量设置为非常大的数字会导致任何异常,比如非常错误的反射角或移动物体的速度错误吗? - Jedi_Maseter_Sam
显示剩余3条评论
1个回答

0

我得到了朋友的帮助,我们设计出了这个算法,使得物体在碰撞时能够反射到不可移动的物体上。

修改后的原始函数:

private static void UpdateEntities(PhysicsEntity a, PhysicsEntity b)
    {
        var collisionAngle = Math.Atan2(a.Position.Y - b.Position.Y, a.Position.X - b.Position.X);

        if (a.IsMoveable && b.IsMoveable)
        {
            var angleA = a.Velocity.Direction - collisionAngle;
            var angleB = b.Velocity.Direction - collisionAngle;

            var vAx = a.Velocity.Magnitude * Math.Cos(angleA);
            var vAy = a.Velocity.Magnitude * Math.Sin(angleA);
            var vBx = b.Velocity.Magnitude * Math.Cos(angleB);
            var vBy = b.Velocity.Magnitude * Math.Sin(angleB);

            var vfAx = ((vAx * (a.Mass - b.Mass) + 2 * b.Mass * vBx) / (a.Mass + b.Mass)) * a.Material.Elasticity;
            var vfBx = ((vBx * (b.Mass - a.Mass) + 2 * a.Mass * vAx) / (a.Mass + b.Mass)) * b.Material.Elasticity;
            var vfAy = vAy * a.Material.Elasticity;
            var vfBy = vBy * b.Material.Elasticity;

            var magA = Math.Sqrt(Math.Pow(vfAx, 2) + Math.Pow(vfAy, 2));
            var magB = Math.Sqrt(Math.Pow(vfBx, 2) + Math.Pow(vfBy, 2));
            var dirA = Math.Atan2(vfAy, vfAx) + collisionAngle;
            var dirB = Math.Atan2(vfBy, vfBx) + collisionAngle;

            a.Velocity.X = magA * Math.Cos(dirA);
            a.Velocity.Y = magA * Math.Sin(dirA);
            b.Velocity.X = magB * Math.Cos(dirB);
            b.Velocity.Y = magB * Math.Sin(dirB);
        }
        else
        {
            var sign = Math.Sign(collisionAngle);
            collisionAngle *= sign;
            while (collisionAngle > Math.PI/2)
            {
                collisionAngle -= Math.PI / 2;
            }
            collisionAngle *= sign;
            if (a.IsMoveable)
            {
                Reflection(ref a, b, collisionAngle);
            }
            else
            {
                Reflection(ref b, a, collisionAngle);
            }
        }
    }

反射函数:

        private static void Reflection(ref PhysicsEntity movable, PhysicsEntity immovable, double collisionAngle)
    {
        if (Math.Abs(collisionAngle - Math.PI / 2) < Universe.Epsilon)
        {
            // take the velocity vector, rotate it 180 degrees, scale it
            movable.Velocity.X *= -1;
            movable.Velocity.Y *= -1;
        }
        else if (Math.Abs(movable.Position.Y - immovable.Position.Y) < Universe.Epsilon ||
                 (movable.Position.X > movable.CollisionPoint.X ^ movable.Position.Y < movable.CollisionPoint.Y))
        {
            //take velocity vector, rotate CCW by 2*collisionAngle, scale it
            var rotateAngle = 2 * collisionAngle;
            var xPrime = movable.Velocity.X * Math.Cos(rotateAngle) - movable.Velocity.Y * Math.Sin(rotateAngle);
            var yPrime = movable.Velocity.Y * Math.Cos(rotateAngle) - movable.Velocity.X * Math.Sin(rotateAngle);
            movable.Velocity.X = xPrime;
            movable.Velocity.Y = yPrime;
        }
        else
        {
            //take the vector, rotate it CCW by 360-2*collisionAngle, scale it
            var rotateAngle = 2 * (Math.PI - collisionAngle);
            var xPrime = movable.Velocity.X * Math.Cos(rotateAngle) - movable.Velocity.Y * Math.Sin(rotateAngle);
            var yPrime = movable.Velocity.Y * Math.Cos(rotateAngle) - movable.Velocity.X * Math.Sin(rotateAngle);
            movable.Velocity.X = xPrime;
            movable.Velocity.Y = yPrime;
        }

        movable.Velocity.X *= movable.Material.Elasticity;
        movable.Velocity.Y *= movable.Material.Elasticity;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接