Algorytm strzelania do celu w grze 3D

11

Dla tych z was, którzy pamiętają Descent Freespace, miała fajną funkcję, która pomaga celować we wroga podczas strzelania pociskami bezkierunkowymi lub laserami: pokazywał celownik przed gonionym statkiem, informujący, gdzie strzelać, aby trafić w ruch cel.

Próbowałem użyć odpowiedzi z /programming/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1, ale próbowałem dostosowując to.

Najpierw zdekomponowałem obliczenia, aby rozwiązać punkt przecięcia dla płaszczyzny XoZ, i zapisałem współrzędne xiz, a następnie rozwiązałem punkt przecięcia dla płaszczyzny XoY i dodałem współrzędną y do końcowego xyz, który następnie przekształciłem do schowka i umieściłem na nich teksturę współrzędne Ale oczywiście to nie działa tak, jak powinno, bo inaczej nie opublikowałbym tego pytania.

Z tego, co zauważam, po znalezieniu x w płaszczyźnie XoZ i XoY, x nie jest takie samo, więc coś musi być nie tak.

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

Pierwszy raz celVelocity.y to tak naprawdę targetVelocity.z (to samo dla targetPos), a drugi raz to właściwie TargVelocity.y.

Ostateczna pozycja po XoZ to

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

i po XoY

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

Czy moje podejście do dzielenia na 2 płaszczyzny i obliczania jest dobre? Czy w przypadku 3D istnieje zupełnie inne podejście?

  • sqr () jest kwadratem, a nie sqrt - unikając zamieszania.
Sebastian Bugiu
źródło
1
„Prowadzenie celu” może być frazą, której szukasz.
MichaelHouse

Odpowiedzi:

12

Nie ma potrzeby dzielenia go na 2 funkcje 2D. Równanie kwadratowe, nad którym pracujesz, działa dobrze również w 3D. Oto pseudo kod dla 2d lub 3d. Oznacza to, że wieża (obrona wieży) strzela pociskiem:

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” może być wektorem, o który pytasz.

Steve H.
źródło
Jesteś geniuszem i uratowałeś mi tyłek !! Cholera, potrzebuję 15 reputacji, aby głosować ....
Sebastian Bugiu,
@SebastianBugiu Zrobiłem to dla Ciebie.
AgentFire,
@SebastianBugiu Dzięki, byłem zadowolony, kiedy nauczyłem się tej koncepcji i cieszę się, że pomogła ci. Kolejną elegancką cechą jest to, że nie trzeba się bawić przy algorytmach wykrywania kolizji. Nie trzeba zapisywać kodu CD. Ponieważ ścieżki celu i pocisków są przewidywalne, wpływ BĘDZIE występować, gdy timeToImpactodlicza się do zera.
Steve H
1

Jest też dobry post na blogu na ten sam temat: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html . Zawiera także bardziej złożone próbki, które obejmują grawitację.

Autor dokonał większego uproszczenia, co skutkuje bardziej zwartym kodem:

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

Aktualizacja: Oryginalny autor wziął pod uwagę tylko większy root. Ale w przypadku, gdy mniejszy korzeń nie jest ujemny, daje to lepsze rozwiązanie, ponieważ czas uderzenia jest krótszy. Odpowiednio zaktualizowałem kod.

Roman Hwang
źródło