反弹旋转边界球

时间:2013-01-24 00:05:49

标签: c# xna geometry

我正在制作一个2d弹球游戏,并使用BoundingSphere作为点击框,作为捷径。

我遇到的问题是很多东西一直在旋转,我需要找出一种方法来计算当球击中其他圆形物体时“准确”的反弹角度。

非常感谢任何帮助,推动和线索

/编辑 找不到任何东西,但设法解决这个问题,有点工作

一旦检测到两个BoundingSphere之间的碰撞,就会调用它。

                private void CollisionRebound(Sprites.BaseSprite attacker, Vector2 defender)
    {
        //Work out the rotation that would result in a "dead on" collision
        //thus rebounding the attacker straight back the way they came.
        float directHitRotation = (float)Math.Atan2(defender.Y - attacker.Position.Y , defender.X - attacker.Position.X);
        //only really needed if the rotation is a negative value but is easier to work from in general.
        float attackerRotation = attacker.rotation;

        //This makes the rotation a positive number, it cant be less that -2PI
        //so adding 2PI will leave us with a positive rotation.
        if (attackerRotation < 0)
        {
            attackerRotation += (float)(Math.PI * 2);
        }


        //If the rotation is greater than the "dead on" rotation the rotation
        //needs to increase.
        if (attackerRotation > directHitRotation)
        {
            //we add "PiOver2" or "90 degrees" to "dead on" rotation because we do, dont know enough
            //trig to explain it just know it works, we then add 90 degrees minus the difference between
            //our two rotation to give us our outgoing angle, the +0.01f is for the rare case where the
            //difference is 90 which would give us no change in rotation but if the two spheres have collided
            //(which they have to before coming to this code chunk) there will be at least some change.
            attackerRotation = directHitRotation + (float)MathHelper.PiOver2 + ((float)MathHelper.PiOver2 -
                (attackerRotation - directHitRotation) + 0.01f);
        }
            //If the rotation is less than the "dead on" rotation the rotation
            //need to decrease.
        else if (attackerRotation < directHitRotation)
        {
            //same as previous chunk but we will be minusing the angle
            attackerRotation = directHitRotation - (float)MathHelper.PiOver2 - ((float)MathHelper.PiOver2 -
                (attackerRotation - directHitRotation) - 0.01f);
        }
        else if (attackerRotation == directHitRotation)
        {
            //either of the two calculations could be used here but would result in the same outcome
            //which is rotating the attacker 180 degrees, so just add 2PI instead.
            attackerRotation += (float)Math.PI;
        }

        //Here we just assign out new output rotation to the attacker entity.
        attacker.rotation = attackerRotation;
    }
只是让“攻击者”偶尔粘在“后卫”身上,有什么建议可以解决这个问题吗?

添加评论以解释有兴趣使用它的其他人的代码。

1 个答案:

答案 0 :(得分:0)

我会对此进行一次尝试,而不是实际尝试,所以我无法保证这是准确的。此外,这是伪代码。

我们需要知道球体之间的碰撞点。如果你在等待每一帧检测到碰撞,那么你的球体可能会部分穿透,所以我要做的第一件事就是把它们推出去。为了做到这一点,你需要知道推动每个人的距离。

Vector3 BtoA = (SphereA.center - SphereB.center);
Vector3 AtoB = (SphereB.center - SphereA.center);

float currentDistance = AtoB.length();

float minimumDistance = SphereA.radius + SphereB.radius;

// If the spheres are interpenetrating then push them apart until 
// they're colliding only at a single point.

// Do a quick sanity check here
if ( currentDistance > minimumDistance )
{
    // Your spheres aren't close enough to be touching, how did you get here?
}
else if ( currentDistance < minimumDistance )
{
    // We move each sphere away by half of the penetration distance.
    float penetrationDistance = currentDistance - minimumDistance;

    Vector3 unitBtoA = BtoA.unitize();
    SphereA.position = SphereA.position + (unitBtoA * penetrationDistance * 0.5f);

    Vector3 unitAtoB = AtoB.unitize();
    SphereB.position = SphereB.position + (unitAtoB * penetrationDistance * 0.5f);

    // Note that now that we have repositioned the spheres they have different AtoB and
    // BtoA vectors, and theoretically could be colliding with spheres very close to
    // them that they weren't colliding with before. We now recalculate our difference vectors
    BtoA = (SphereA.center - SphereB.center);
    AtoB = (SphereB.center - SphereA.center);
}

// Ok, now we know that the spheres are only touching at one point. We can now calculate
// the reflection/deflection

// I believe the code for calculating a deflection of a velocity off of a surface
// given the normal of that surface is something like this. This assumes no energy
// is lost on the bounce as well, which isn't realistic.
Vector3 Reflect( Vector3 velocity, Vector3 surfaceNormal )
{
    const float dotProductTimesTwo = velocity.Dot(surfaceNormal) * 2.0f; 
    velocity.x -= dotProductTimesTwo * surfaceNormal.x;
    velocity.y -= dotProductTimesTwo * surfaceNormal.y;
    velocity.z -= dotProductTimesTwo * surfaceNormal.z;
}

// Using the above function, we reflect the velocities of both spheres
Vector3 unitBtoA = BtoA.unitize();
Vector3 unitAtoA = AtoA.unitize();

SphereA.velocity = Reflect( SphereA.velocity, unitBtoA );
SphereB.velocity = Reflect( SphereA.velocity, unitAtoB );

如果你想要更准确地反弹,你应该能够根据球体的穿透深度找出,如果你没有使用基于框架的应用程序,它们会因碰撞而经过多长时间。这应该可以让你计算他们现在彼此反弹的距离。如果你计算那个时间,那么你可以采用我们在上面计算的球体速度,并修改球体在这段时间内的位置。

// This variable would hold the amount of time since the spheres actually would have collided
float extra_time;

SphereA.position = SphereA.position + (SphereA.velocity * extra_time);
SphereB.position = SphereB.position + (SphereB.velocity * extra_time);

正如我之前所说,我没有测试过这段代码,所以它可能根本不起作用,但至少这对你来说可能是一个很好的起点,或者可能接近功能。希望这会有所帮助。祝你好运。