Algorithme pour tirer sur une cible dans un jeu 3D

11

Pour ceux d'entre vous qui se souviennent de Descent Freespace, il avait une fonctionnalité intéressante pour vous aider à viser l'ennemi lorsque vous tirez des missiles ou des lasers non-homing: il montrait un réticule devant le navire que vous avez poursuivi vous disant où tirer pour frapper le mouvement cible.

J'ai essayé d'utiliser la réponse de /programming/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1 mais c'est pour 2D, donc j'ai essayé l'adapter.

J'ai d'abord décomposé le calcul pour résoudre le point d'intersection pour le plan XoZ et enregistré les coordonnées x et z, puis résolu le point d'intersection pour le plan XoY et ajouté la coordonnée y à un xyz final que j'ai ensuite transformé en espace de clips et mis une texture à ces coordonnées. Mais bien sûr, cela ne fonctionne pas comme il se doit, sinon je n'aurais pas posté la question.

D'après ce que j'ai remarqué après avoir trouvé x dans le plan XoZ et dans XoY, le x n'est pas le même, donc quelque chose doit être faux.

    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 première fois targetVelocity.y est en fait targetVelocity.z (la même chose pour targetPos) et la deuxième fois c'est en fait targetVelocity.y.

La position finale après XoZ est

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

et après XoY

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

Mon approche de la séparation en 2 plans et du calcul est-elle bonne? Ou pour la 3D, il y a une approche complètement différente?

  • sqr () est carré et non sqrt - évitant ainsi toute confusion.
Sebastian Bugiu
la source
1
"Diriger la cible" est peut-être l'expression que vous recherchez.
MichaelHouse

Réponses:

12

Il n'est pas nécessaire de le décomposer en 2 fonctions 2D. Cette équation quadratique avec laquelle vous travaillez fonctionne également bien en 3D. Voici un pseudo code pour 2d ou 3d. Cela implique qu'une tour (tower defense) tire sur le projectile:

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' peut être le vecteur dont vous parlez.

Steve H
la source
Tu es un génie et j'ai sauvé mon cul !! Merde, j'ai besoin d'une réputation de 15 pour voter ...
Sebastian Bugiu
@SebastianBugiu je l'ai fait pour vous.
AgentFire
@SebastianBugiu Merci, j'étais heureux quand j'ai appris ce concept et je suis content que cela vous ait aidé. Une autre caractéristique élégante est que vous n'avez pas besoin de jouer avec les algorithmes de détection de collision. Aucun code CD n'a besoin d'être écrit. Étant donné que les trajectoires des cibles et des projectiles sont prévisibles, l'impact se produira lors du timeToImpactdécompte à zéro.
Steve H
1

Il existe également un bon article de blog sur le même sujet: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html . Il contient également des échantillons plus complexes qui incluent la gravité.

L'auteur a fait plus de simplification, ce qui donne un code plus compact:

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

Mise à jour: l' auteur d'origine ne tenait compte que d'une racine plus importante. Mais dans le cas où une racine plus petite n'est pas négative, il en résulte une meilleure solution, car le temps d'impact est plus petit. J'ai mis à jour le code en conséquence.

Roman Hwang
la source