Программирование плавного изменения тяги от текущего вектора скорости к целевому вектору

StackOverflow https://stackoverflow.com/questions/1365415

Вопрос

TL; dr:"Я не уверен, как рассчитать плавный переход тяги между одним вектором и другим".

Я программирую простую игру, в которой враг преследует игрока на открытом пространстве (без стен).Я вычислял скорости противника по оси x и y независимо, ускоряя их, если они двигались в направлении игрока, и быстро замедляя, если они двигались не в ту сторону (напримерСкорость врага.x > 0 и игрок.x < enemy.x, затем EnemyVelocity.x - 2.)

В то время как игровой процесс доставляет приличное удовольствие, пытаясь увернуться от врага, я хочу, чтобы враг вел себя с использованием надлежащей физики.Что я сейчас делаю, так это заставляю врага устанавливать свою тягу (например, космический корабль) на основе угла между ним и игроком, и заставляю его тягу ускоряться до максимальной скорости (вычисляя сторону c треугольника вражеской скорости).Как только это произойдет, я не уверен, что это лучший способ настроить тягу самостоятельно.Если я не оставлю максимальную скорость, противник хорошо разгоняется, но легко обгонит игрока, а затем потребуется много времени, чтобы набрать достаточный импульс и вернуться в направлении игрока.

Чего бы я хотел, так это чтобы враг постоянно регулировал свою скорость на пути к игроку, нацеливаясь туда, где он находится (я не хочу, чтобы они предсказывали, где вы будете).Затем, когда они промахиваются мимо игрока, я бы хотел, чтобы те же формулы тяги и ускорения скорректировали их скорость и отправили их обратно в игрока.

Я думаю, что это будет включать в себя два вектора:тот, где враг находится в данный момент, и тот, куда враг хочет отправиться (вектор, который приведет их прямо к игроку).Я не уверен, как рассчитать плавный переход тяги между одним вектором и другим.

Любые советы, формулы или вопросы будут высоко оценены!Спасибо вам за переполнение стека.

Это было полезно?

Решение

Все сводится к уравнениям Ньютона:

F = m * a
s = s_o + v * t + a * t^2 / 2
v = v_o + a * t

В данном случае F является ли сила (тяга), a является ускорением, и m это масса корабля. s является текущим местоположением, s_o является исходным местоположением, v является скоростью, и t это текущее время.

Конечно, это происходит по прямой линии, поэтому, если вы хотите преобразовать в два или три измерения, вам придется немного посчитать. F, s, v, и a являются всеми векторами, что означает, что их направление одинаково важно.Технически t это тоже вектор, но поскольку время обычно течет только в одном направлении, нам не нужно беспокоиться об этом.

2d:
F^2 = F_x^2 + F_y^2 (use Pythagorean theorem to split force into components)
F_x = m * a_x
F_y = m * a_y
s_x = s_o_x + v_x * t + a_x * t^2 / 2
s_y = s_o_y + v_y * t + a_y * t^2 / 2
v_x = v_o_x + a_x * t
v_y = v_o_y + a_y * t

3d:
F^2 = F_x^2 + F_y^2 + F_z^2 (surprisingly, this works)
F_x = m * a_x
F_y = m * a_y
F_z = m * a_z
s_x = s_o_x + v_x * t + a_x * t^2 / 2
s_y = s_o_y + v_y * t + a_y * t^2 / 2
s_z = s_o_z + v_z * t + a_z * t^2 / 2
v_x = v_o_x + a_x * t
v_y = v_o_y + a_y * t
v_z = v_o_z + a_z * t

Теперь, чтобы настроить свою скорость в соответствии с направлением игрока, у вас есть фиксированная общая сила (F) для того, чтобы изменить вашу текущую скорость по отношению к игроку.В физике все происходит не мгновенно, но ваша цель должна состоять в том, чтобы свести к минимуму время, за которое происходит изменение ('t').

Это дает вам уравнение в терминах вашего текущего местоположения ((s_o_x,s_o_y) или (s_o_x,s_o_y,s_o_z)) и текущее местоположение вашего противника или ваше целевое местоположение ((s_x,s_y) или (s_x,s_y,s_z)), для вашей целевой скорости (игнорирует ускорение).

v_x = (s_x - s_o_x) / t
v_y = (s_y - s_o_y) / t

v_x = (s_x - s_o_x) / t
v_y = (s_y - s_o_y) / t
v_z = (s_z - z_o_y) / t

Мы можем заменить это нашим другим уравнением:

(s_x - s_o_x) / t = v_o_x + a_x * t
(s_y - s_o_y) / t = v_o_y + a_y * t

(s_x - s_o_x) / t = v_o_x + a_x * t
(s_y - s_o_y) / t = v_o_y + a_y * t
(s_z - z_o_y) / t = v_o_z + a_z * t

Затем мы решаем для ускорения (это связано с силой, которую мы пытаемся вычислить).

(s_x - s_o_x) / t^2 - v_o_x / t = a_x
(s_y - s_o_y) / t^2 - v_o_y / t = a_y

(s_x - s_o_x) / t^2 - v_o_x / t = a_x
(s_y - s_o_y) / t^2 - v_o_y / t = a_y
(s_z - z_o_y) / t^2 - v_o_z / t = a_z

Включите это в уравнение силы:

F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t

F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t
F_z = m * (s_z - z_o_y) / t^2 - m * v_o_z / t

Теперь решите для t:

t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y

t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
t = (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z

Времена должны сходиться, так что времена будут равны!Это дает нам систему уравнений для каждой координаты (плоскости и сферы).Обратите внимание, что существует несколько возможных значений, но некоторые из них включают мнимые числа, поэтому вам придется исключить эти решения:

(-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
= (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
F^2 = F_x^2 + F_y^2

(-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
= (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
= (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z
F^2 = F_x^2 + F_y^2 + F_z^2

Решите для (F_x,F_y) или (F_x,F_y,F_z) координаты - и у вас есть силы, которые вам нужны.

Дайте мне знать, если у вас возникнут какие-либо вопросы или вы обнаружите ошибки в моей математике.

Другие советы

Вы можете получить желаемый эффект, обеспечив плавное изменение скорость, а не толчок.Таким образом, если противник промахнется мимо игрока, он может немедленно отменить свое ускорение, что замедлит его и, в конечном счете, изменит направление движения.

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

while (game_in_progress)
{
    // Distance from enemy to player.  The larger the
    // distance, the greater the acceleration will be.
    delta.x = player.x - enemy.x
    delta.y = player.y - enemy.y

    // Accelerate by changing velocity based on distance,
    // where 'scale' is sufficiently small. (Limit v to
    // some maximum if you choose; likely to be unnecessary.)
    v.x += delta.x * scale
    v.y += delta.y * scale

    // Update the enemy's position.
    enemy.x += v.x
    enemy.y += v.y
}

Путем вычисления x и y независимо от значений вы можете избавить себя от головной боли при работе с векторами, углами и одновременными уравнениями.

Аналогичным образом, признавая, что ускорение (тяга) - это просто изменение скорости, которая, в свою очередь, является изменением положения, вы можете создать симуляцию в дискретном времени, используя только простую алгебру вместо математического анализа.

Получайте удовольствие!

Вам нужно мыслить в правильных физических терминах.У вас есть скорость, и вы хотите добавить ускорение.Вот и все, что от этого требуется - ускорение - это постепенное изменение скорости, которое привлечет противника к игроку, позволит ему промахнуться, замедлиться (или развернуться), а затем направиться обратно к игроку.

Ускорение измеряется как d (скорость)/время.Вы хотите ускориться по направлению к игроку в любой момент времени, поэтому каждый интервал (секунда, сотая доля секунды или что бы вы ни выбрали) вам нужно добавлять к вашей скорости вектор между врагом и игроком, умноженный на некоторую константу.

Velocity = Velocity + c * (Player-Enemy vector)

Постоянная c будет зависеть от того, с какой скоростью вы хотите ускоряться по направлению к проигрывателю и как часто вы обновляете свою скорость.

Если вы хотите "ограничить" максимальную скорость противника, чтобы он не продолжал увеличивать величину своей скорости бесконечно, вы также можете это сделать.

Velocity = Velocity * (Maximum magniture / |Velocity|)

Редактировать:чтобы пояснить далее, добавление Скорости просто означает добавление составляющих векторов.Итак

Vx = Vx + c * Ax
Vy = Vy + c * Ay

где V - скорость, а A - ускорение.Величина измеряется как sqrt(Vx^2 + Vy^2), то есть гипотенуза прямоугольного треугольника.Итак, если вы хотите, чтобы максимальная скорость противника была m,

Vx = Vx * ( m / sqrt(Vx^2 + Vy^2)
Vy = Vy * ( m / sqrt(Vx^2 + Vy^2)

Некоторое время назад я написал простую игру asteroids, в которой был корабль "союзника", который выслеживал астероиды и стрелял по ним для вас.По сути, он обнаружил ближайший астероид, затем начал плавно поворачиваться к нему и следовать за ним.К сожалению, у меня больше нет кода, но, если мне не изменяет память, я думаю, что я немного поворачивал корабль при каждом повороте, затем, если астероид был далеко, я ускорялся, но если он был близко, я старался соответствовать скорости астероида.На самом деле это было довольно круто, и в нем был задействован минимум алгебры.

Лучшим способом сделать это было бы взять 2 значения в радианах и распределить их между собой, обработав перенос.(возможно, путем добавления или вычитания 2pi там, где это необходимо).Затем преобразуйте его в единичный вектор.Впоследствии умножьте это на скорость, которую вы хотите, чтобы корабль разогнался, и вот оно!

Один простой способ (не соответствующий физике) - вычислить "желаемую скорость" вашего врага, а затем скорректировать текущую скорость противника в соответствии с ней, учитывая любые ограничения на максимальной или минимальной скорости, которые у него есть.

Например, в небольшой 2d-игре, которую я написал (http://wordwarvi.sourceforge.net) есть "ракеты с тепловым наведением". Выглядит довольно странно, если ракеты останавливаются в воздухе, чтобы развернуться.Итак, что я сделал, было следующим:Я вычисляю "желаемую скорость", которая направлена к игроку.Это просто делается с помощью "похожих треугольников".Я нахожу расстояние до игрока в X и в Y, и в каком случае больше, я делаю "желаемую (x или y) скорость максимально возможной", а затем масштабирую другую, чтобы соответствовать "подобному треугольнику". Обратите внимание, что это всего лишь "желаемая скорость", а не текущая скорость.Я беру текущую скорость и медленно корректирую ее (немного за кадр) в направлении "желаемой" скорости (хотя желаемая скорость также пересчитывается за кадр), учитывая минимумы img для vx и vy, чтобы они не останавливались в воздухе.

Тупой алгоритм, но он работает нормально (никто не жаловался, что они слишком просты, слишком сложны или слишком нереалистичны).

Редактировать:перечитав вопрос, я понял, что мой ответ, вероятно, не то, что вам нужно.

Я профессионально решал подобные проблемы, и я советую вам начать с простых версий и доработать.Убедитесь, что вы получаете ожидаемое поведение на каждом шаге, прежде чем переходить к следующему.

  1. Искатель ищет неподвижную цель в начале координат в одном измерении.Это верно, одно измерение.Он может перемещаться взад и вперед по оси x и пытается добраться до x = 0.Двигатель не имеет дроссельной заслонки (как у твердотопливной ракеты), но искатель может направить его в любом направлении.Если вы правильно это запрограммируете, искатель будет колебаться вокруг x = 0, каждый раз промахиваясь.
  2. То же самое, но цель неподвижна где-то еще, кроме x = 0.Просто сделайте x относительным, а не абсолютным (то есть ищущий заботится о разница в x, а не в x цели).
  3. Теперь цель движется (или прыгает).Ищущий должен быть способен следовать за ним повсюду.Колебания будут увеличиваться или уменьшаться в зависимости от того, как движется цель - вы поймете, что я имею в виду.
  4. Теперь два измерения.Искатель всегда направляет удар прямо на цель, что означает, что вы должны разделить удар на составляющие x и y простым тригонометрическим методом.Если вы переместите цель, искатель может выйти на орбиту вокруг нее.
  5. Возвращаемся в одно измерение и к неподвижной цели, но теперь "искатель" пытается встретиться, а не пролететь мимо.Это самая трудная часть.Цель состоит в том, чтобы расстояние и скорость одновременно стали равны нулю, без превышения, поэтому искатель должен знать свою собственную способность к торможению.Когда x меньше, чем v ^ 2/2a, искатель должен изменить тягу, толкая прочь от цели, чтобы замедлиться и встретить ее.Приятно позволить искателю прекратить нанесение удара, когда он находится очень близко к цели.
  6. Цель снова начинает двигаться.Это очень просто;просто сделайте x и v относительными, а не абсолютными.
  7. Множество измерений.Это удивительно просто;части x, y и z независимы.

Теперь, если вам нужен искатель, который не может включиться ни на грош, но должен кривая вокруг все становится сложнее...

Есть всего несколько советов, как сделать это правильно и легко.1) проще всего и наиболее обобщенно работать с векторами, чем писать все два или три раза.2) все будет выглядеть правильно, если вы будете контролировать силу (которая фактически является ускорением, поскольку A = F / масса), а затем динамически изменять скорость и положение.

Ваш базовый цикл для реалистичного движения выглядит следующим образом (где заглавные буквы - векторы, а dt - ваш временной шаг)::

while (motion) {
   A = get_acceleration(X, V, A, X_target, V_targer, A_target)
   V += A*dt       // V is the integral of A
   X += V*dt       // X is the integral of V
}

И действительно, это как раз то, что нужно для вашей динамичной эволюции.

Затем вам нужно решить, как определить ваше ускорение, т.е.написать get_acceleration.Здесь есть несколько вариантов, которые зависят от множества факторов, и реальные охотники используют несколько стратегий.Например, если у вас большая тяга относительно вашей массы (т.е.высокое ускорение) вы, вероятно, просто хотите направиться прямо к цели;но если у вас большая масса по сравнению с вашей тягой, вы, вероятно, захотите взять курс на перехват.Если вы хотите замедлиться по мере приближения к цели, вы могли бы отменить ускорение, когда |X-X_target| становится маленьким (т.е.они приближаются) и / или их скорости близки.Кроме того, демпфирование может помочь вещам не колебаться, и для этого добавьте термин к ускорению что-то вроде -c*(V-V_target).Я предлагаю вам поиграться с ними, пока не получите что-то, соответствующее внешнему виду и ощущениям, к которым вы стремитесь.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top