Программирование плавного изменения тяги от текущего вектора скорости к целевому вектору
-
21-09-2019 - |
Вопрос
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, чтобы они не останавливались в воздухе.
Тупой алгоритм, но он работает нормально (никто не жаловался, что они слишком просты, слишком сложны или слишком нереалистичны).
Редактировать:перечитав вопрос, я понял, что мой ответ, вероятно, не то, что вам нужно.
Я профессионально решал подобные проблемы, и я советую вам начать с простых версий и доработать.Убедитесь, что вы получаете ожидаемое поведение на каждом шаге, прежде чем переходить к следующему.
- Искатель ищет неподвижную цель в начале координат в одном измерении.Это верно, одно измерение.Он может перемещаться взад и вперед по оси x и пытается добраться до x = 0.Двигатель не имеет дроссельной заслонки (как у твердотопливной ракеты), но искатель может направить его в любом направлении.Если вы правильно это запрограммируете, искатель будет колебаться вокруг x = 0, каждый раз промахиваясь.
- То же самое, но цель неподвижна где-то еще, кроме x = 0.Просто сделайте x относительным, а не абсолютным (то есть ищущий заботится о разница в x, а не в x цели).
- Теперь цель движется (или прыгает).Ищущий должен быть способен следовать за ним повсюду.Колебания будут увеличиваться или уменьшаться в зависимости от того, как движется цель - вы поймете, что я имею в виду.
- Теперь два измерения.Искатель всегда направляет удар прямо на цель, что означает, что вы должны разделить удар на составляющие x и y простым тригонометрическим методом.Если вы переместите цель, искатель может выйти на орбиту вокруг нее.
- Возвращаемся в одно измерение и к неподвижной цели, но теперь "искатель" пытается встретиться, а не пролететь мимо.Это самая трудная часть.Цель состоит в том, чтобы расстояние и скорость одновременно стали равны нулю, без превышения, поэтому искатель должен знать свою собственную способность к торможению.Когда x меньше, чем v ^ 2/2a, искатель должен изменить тягу, толкая прочь от цели, чтобы замедлиться и встретить ее.Приятно позволить искателю прекратить нанесение удара, когда он находится очень близко к цели.
- Цель снова начинает двигаться.Это очень просто;просто сделайте x и v относительными, а не абсолютными.
- Множество измерений.Это удивительно просто;части 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)
.Я предлагаю вам поиграться с ними, пока не получите что-то, соответствующее внешнему виду и ощущениям, к которым вы стремитесь.