4

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

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

任何帮助,轻推和线索将不胜感激

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

一旦检测到两个 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;
    }

只是偶尔让“攻击者”粘在“防守者”身上,有什么解决这个问题的建议吗?

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

4

1 回答 1

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

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

于 2013-03-14T04:19:21.403 回答