Algoritmo para disparar a un objetivo en un juego en 3D

11

Para aquellos de ustedes que recuerdan Descent Freespace, tenía una buena característica que los ayudaba a apuntar al enemigo cuando disparaban misiles o láseres no orientados: mostraba un punto de mira frente al barco que perseguían diciéndoles dónde disparar para golpear el movimiento objetivo.

Intenté usar la respuesta de /programming/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1 pero es para 2D, así que intenté adaptándolo

Primero descompuse el cálculo para resolver el punto de intersección para el plano XoZ y guardé las coordenadas xyz y luego resolví el punto de intersección para el plano XoY y agregué la coordenada y a un xyz final que luego transformé en el espacio de clips y puse una textura en esos coordenadas Pero, por supuesto, no funciona como debería o de lo contrario no habría publicado la pregunta.

Por lo que noté después de encontrar x en el plano XoZ y en XoY, la x no es la misma, por lo que algo debe estar mal.

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

La primera vez que targetVelocity.y es en realidad targetVelocity.z (lo mismo para targetPos) y la segunda vez es en realidad targetVelocity.y.

La posición final después de XoZ es

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

y después de XoY

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

¿Mi enfoque de separar en 2 planos y calcular es bueno? ¿O para 3D hay un enfoque completamente diferente?

  • sqr () es cuadrado, no sqrt, evitando confusiones.
Sebastian Bugiu
fuente
1
"Liderando el objetivo" puede ser la frase que estás buscando.
MichaelHouse

Respuestas:

12

No hay necesidad de dividirlo en 2 funciones 2d. Esa ecuación cuadrática con la que está trabajando también funciona bien en 3d. Aquí hay un pseudocódigo para 2d o 3d. Implica que una torre (defensa de la torre) está disparando el proyectil:

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' puede ser el vector sobre el que está preguntando.

Steve H
fuente
Eres un genio y me salvaste el culo !! Maldición, necesito una reputación de 15 para votar ...
Sebastian Bugiu
@SebastianBugiu lo hice por ti.
AgentFire el
@SebastianBugiu Gracias, me alegré cuando aprendí este concepto y me alegra que te haya ayudado. Otra característica elegante es que no necesita perder el tiempo con los algoritmos de detección de colisiones. No es necesario escribir ningún código de CD. Dado que las rutas de objetivo y proyectil son predecibles, el impacto se producirá cuando la timeToImpactcuenta regresiva a cero.
Steve H
1

También hay una buena publicación de blog sobre el mismo tema: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html . También contiene muestras más complejas que incluyen la gravedad.

El autor ha hecho más simplificación, lo que da como resultado un código más compacto:

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

Actualización: el autor original solo tuvo en cuenta la raíz más grande. Pero en caso de que la raíz más pequeña no sea negativa, da como resultado una mejor solución, ya que el tiempo de impacto es menor. He actualizado el código correspondientemente.

Roman Hwang
fuente