Алгоритм стрельбы по цели в 3d игре

11

Для тех из вас, кто помнит Descent Freespace, у него была хорошая функция, позволяющая вам прицелиться в противника при стрельбе из самонаводящихся ракет или лазеров: он показал перекрестие перед преследуемым вами кораблем, указывающее, куда стрелять, чтобы поразить движущееся цель.

Я попытался использовать ответ из /programming/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1, но это для 2D, поэтому я попытался адаптируя это.

Сначала я разложил вычисление, чтобы найти точку пересечения для плоскости XoZ, сохранил координаты x и z, а затем решил точку пересечения для плоскости XoY и добавил координату y к окончательному xyz, который я затем преобразовал в пространство клипов, и поместил в них текстуру. координаты. Но, конечно, это не работает так, как должно, иначе я бы не отправил вопрос.

Из того, что я заметил, после нахождения x в плоскости XoZ и в XoY x не совпадает, поэтому что-то должно быть не так.

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

Первый раз targetVelocity.y на самом деле является targetVelocity.z (то же самое для targetPos), а второй раз - targetVelocity.y.

Финальная позиция после XoZ

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

и после XoY

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

Мой подход разделения на 2 плоскости и вычисления какой-либо пользы? Или для 3D есть совершенно другой подход?

  • sqr () является квадратом, а не sqrt - избегая путаницы.
Себастьян Бугиу
источник
1
«Ведущий цель» может быть фразой, которую вы ищете.
MichaelHouse

Ответы:

12

Нет необходимости разбивать его на 2 2d функции. Это квадратное уравнение, с которым вы работаете, прекрасно работает и в 3d. Вот псевдокод для 2d или 3d. Это означает, что башня (защита башни) стреляет из снаряда:

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' может быть вектором, о котором вы спрашиваете.

Стив Н
источник
Ты гений и спас мою задницу !! Черт, мне нужно 15 репутации, чтобы поднять голос ...
Себастьян Бугиу
@SebastianBugiu я сделал это для тебя.
AgentFire
@SebastianBugiu Спасибо, я был рад, когда узнал эту концепцию, и рад, что она помогла вам. Еще одна изящная особенность: вам не нужно возиться с алгоритмами обнаружения столкновений. Код CD не нужно писать. Поскольку пути цели и снаряда предсказуемы, воздействие будет происходить при timeToImpactобратном отсчете до нуля.
Стив Х
1

Есть также хороший пост в блоге на ту же тему: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html . Он также содержит более сложные образцы, которые включают гравитацию.

Автор сделал больше упрощений, что приводит к более компактному коду:

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

Обновление: оригинальный автор учел только больший рут. Но в случае, если меньший корень неотрицателен, это приводит к лучшему решению, так как время воздействия меньше. Я обновил код соответственно.

Роман Хван
источник