Algoritmo para atirar em um alvo em um jogo em 3D

11

Para aqueles que se lembram do Descent Freespace, ele tinha um recurso interessante para ajudá-lo a mirar no inimigo ao disparar mísseis ou lasers que não sejam do local: mostrava uma mira na frente do navio que você perseguia, dizendo onde atirar para acertar o movimento alvo.

Eu tentei usar a resposta de /programming/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1, mas é para 2D, então tentei adaptando-o.

Decompii primeiro o cálculo para resolver o ponto de interseção para o plano XoZ e salvei as coordenadas x e z e resolvi o ponto de interseção para o plano XoY e adicionei a coordenada y a um xyz final que depois transformei no espaço de clipe e coloquei uma textura naqueles coordenadas. Mas é claro que não funciona como deveria, ou então eu não teria postado a pergunta.

Pelo que notei, depois de encontrar x no plano XoZ e no XoY o x não é o mesmo, então algo deve estar errado.

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

A primeira vez que targetVelocity.y é realmente targetVelocity.z (o mesmo para targetPos) e na segunda vez é realmente targetVelocity.y.

A posição final após XoZ é

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

e depois de XoY

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

Minha abordagem de separar em dois planos e calcular algum bem? Ou para 3D, existe uma abordagem totalmente diferente?

  • sqr () é quadrado, não sqrt - evitando confusão.
Sebastian Bugiu
fonte
1
"Liderar o alvo" pode ser a frase que você está procurando.
MichaelHouse

Respostas:

12

Não há necessidade de dividi-lo em 2 funções 2d. Essa equação quadrática com a qual você está trabalhando também funciona bem em 3d. Aqui está o pseudo-código para 2D ou 3D. Isso implica que uma torre (defesa) está atirando no projétil:

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' pode ser o vetor que você está perguntando.

Steve H
fonte
Você é um gênio e salvou minha bunda !! Porra, preciso de 15 reputação a upvote ....
Sebastian Bugiu
@SebastianBugiu eu fiz isso por você.
AgentFire
@SebastianBugiu Obrigado, fiquei feliz quando aprendi esse conceito e fico feliz por ter ajudado você. Outra característica elegante é que você não precisa mexer nos algoritmos de detecção de colisão. Nenhum código de CD precisa ser gravado. Como os caminhos dos alvos e dos projéteis são previsíveis, o impacto ocorrerá quando a timeToImpactcontagem for zero.
9788 Steve
1

Há também uma boa publicação no blog sobre o mesmo assunto: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html . Ele também contém amostras mais complexas que incluem gravidade.

O autor fez mais simplificação, o que resulta em um código mais 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;
}

Atualização: o autor original levou em conta apenas uma raiz maior. Porém, no caso de uma raiz menor não ser negativa, resulta em melhor solução, pois o tempo de impacto é menor. Eu atualizei o código correspondentemente.

Roman Hwang
fonte