4

私は 2D ピンボール ゲームを作成しており、ショートカットとしてヒット ボックスに BoundingSphere を使用しています。

私が抱えている問題は、多くのものが常に回転しており、ボールが他の円形のオブジェクトに当たったときの「正確な」反発角度を計算する方法を考え出す必要があることです。

どんな助け、ナッジ、手がかりも大歓迎です

/編集何も見つかりませんでしたが、なんとかこれを解決しました。

これは、2 つの 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 に答える