Algorithmus zum Schießen auf ein Ziel in einem 3D-Spiel


11

Für diejenigen unter Ihnen, die sich an Descent Freespace erinnern, hatte es eine nette Funktion, mit der Sie auf den Feind zielen konnten, wenn Sie Raketen oder Laser ohne Zielort abschossen: Es zeigte ein Fadenkreuz vor dem Schiff, das Sie gejagt hatten, und sagte Ihnen, wo Sie schießen müssen, um die Bewegung zu treffen Ziel.

Ich habe versucht, die Antwort von /programming/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1 zu verwenden, aber es ist für 2D, also habe ich es versucht Anpassung.

Ich habe zuerst die Berechnung zerlegt, um den Schnittpunkt für die XoZ-Ebene zu lösen, die x- und z-Koordinaten gespeichert und dann den Schnittpunkt für die XoY-Ebene gelöst und die y-Koordinate zu einem endgültigen xyz hinzugefügt, das ich dann in den Clipspace transformiert und dort eine Textur eingefügt habe Koordinaten. Aber natürlich funktioniert es nicht so, wie es sollte, sonst hätte ich die Frage nicht gestellt.

Nach dem, was ich nach dem Finden von x in der XoZ-Ebene und dem in XoY bemerke, ist das x nicht dasselbe, also muss etwas falsch sein.

    float a = ENG_Math.sqr(targetVelocity.x) + ENG_Math.sqr(targetVelocity.y) -
            ENG_Math.sqr(projectileSpeed);
    float b = 2.0f * (targetVelocity.x * targetPos.x + 
            targetVelocity.y * targetPos.y);
    float c = ENG_Math.sqr(targetPos.x) + ENG_Math.sqr(targetPos.y);
    ENG_Math.solveQuadraticEquation(a, b, c, collisionTime);

Das erste Mal ist targetVelocity.y tatsächlich targetVelocity.z (dasselbe für targetPos) und das zweite Mal ist es tatsächlich targetVelocity.y.

Die endgültige Position nach XoZ ist

    crossPosition.set(minTime * finalEntityVelocity.x + finalTargetPos4D.x, 0.0f, 
                minTime * finalEntityVelocity.z + finalTargetPos4D.z);

und nach XoY

    crossPosition.y = minTime * finalEntityVelocity.y + finalTargetPos4D.y;

Ist mein Ansatz, in zwei Ebenen zu trennen und etwas Gutes zu berechnen? Oder gibt es für 3D einen ganz anderen Ansatz?

  • sqr () ist ein Quadrat, kein sqrt - um Verwirrung zu vermeiden.

1
"Das Ziel führen" könnte der Ausdruck sein, nach dem Sie suchen.
MichaelHouse

Antworten:


12

Es ist nicht erforderlich, es in 2 2D-Funktionen zu unterteilen. Diese quadratische Gleichung, mit der Sie arbeiten, funktioniert auch in 3D einwandfrei. Hier ist Pseudocode für 2d oder 3d. Dies impliziert, dass ein Turm (Turmverteidigung) auf das Projektil schießt:

Vector totarget =  target.position - tower.position;

float a = Vector.Dot(target.velocity, target.velocity) - (bullet.velocity * bullet.velocity);
float b = 2 * Vector.Dot(target.velocity, totarget);
float c = Vector.Dot(totarget, totarget);

float p = -b / (2 * a);
float q = (float)Math.Sqrt((b * b) - 4 * a * c) / (2 * a);

float t1 = p - q;
float t2 = p + q;
float t;

if (t1 > t2 && t2 > 0)
{
    t = t2;
}
else
{
    t = t1;
}

Vector aimSpot = target.position + target.velocity * t;
Vector bulletPath = aimSpot - tower.position;
float timeToImpact = bulletPath.Length() / bullet.speed;//speed must be in units per second 

'aimSpot' ist möglicherweise der Vektor, nach dem Sie fragen.


Du bist ein Genie und hast meinen Arsch gerettet !! Verdammt, ich brauche einen Ruf von 15, um mich zu verbessern ...
Sebastian Bugiu

@SebastianBugiu Ich habe es für dich getan.
AgentFire

@SebastianBugiu Danke, ich war froh, als ich dieses Konzept gelernt habe und bin froh, dass es Ihnen geholfen hat. Ein weiteres elegantes Merkmal ist, dass Sie nicht mit Kollisionserkennungsalgorithmen herumspielen müssen. Es muss kein CD-Code geschrieben werden. Da Ziel- und Projektilpfade vorhersehbar sind, tritt die Auswirkung auf, wenn timeToImpactbis Null heruntergezählt wird.
Steve H

1

Es gibt auch einen guten Blog-Beitrag zum gleichen Thema: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html . Es enthält auch komplexere Proben, die die Schwerkraft enthalten.

Der Autor hat mehr Vereinfachungen vorgenommen, was zu kompakterem Code führt:

double time_of_impact(double px, double py, double vx, double vy, double s)
{
    double a = s * s - (vx * vx + vy * vy);
    double b = px * vx + py * vy;
    double c = px * px + py * py;

    double d = b*b + a*c;

    double t = 0;
    if (d >= 0)
    {
        t = (b - sqrt(d)) / a;
        if (t < 0) 
        {
            t = (b + sqrt(d)) / a;
            if (t < 0)
                t = 0;
        }
    }

    return t;
}

Update: Der ursprüngliche Autor berücksichtigte nur größere Wurzeln. Wenn jedoch eine kleinere Wurzel nicht negativ ist, führt dies zu einer besseren Lösung, da die Aufprallzeit kürzer ist. Ich habe den Code entsprechend aktualisiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.