2d траектория планирования космического корабля с физикой

Я реализую 2D-игру с кораблями в космосе.

Чтобы сделать это, я использую LÖVE, который обертывает Box2D с помощью Lua. Но я считаю, что на мой вопрос может ответить любой, кто лучше понимает физику, чем я, - поэтому псевдокод принимается как ответ.

Моя проблема в том, что я не знаю, как правильно поместить мои космические корабли в мир с физикой, поддерживающей физику. Более конкретно:

Корабль массы m находится в исходном положении {x, y}. Он имеет начальный вектор скорости {vx, vy} (может быть {0,0}).

Цель - это точка в {xo,yo}. Корабль должен достичь цели, имеющей скорость {vxo, vyo} (или около нее), после кратчайшей траектории.

Существует функция, называемая update(dt), которая часто называется (т.е. 30 раз в секунду). По этой функции корабль может изменять свое положение и траекторию, применяя "импульсы" к себе. Величина импульсов двоичная: вы можете применить ее в заданном направлении или вообще не применять). В коде это выглядит так:

function Ship:update(dt)
  m = self:getMass()
  x,y = self:getPosition()
  vx,vy = self:getLinearVelocity()
  xo,yo = self:getTargetPosition()
  vxo,vyo = self:getTargetVelocity()
  thrust = self:getThrust()

  if(???)
    angle = ???
    self:applyImpulse(math.sin(angle)*thrust, math.cos(angle)*thrust))
  end
end

Первый ??? указывает, что в некоторых случаях (я думаю) было бы лучше "не поимствовать" и оставить "дрейф" корабля. Вторая часть ??? состоит в том, как рассчитать угол импульса на заданном dt.

Мы находимся в пространстве, поэтому мы можем игнорировать такие вещи, как трение воздуха.

Хотя было бы очень приятно, я не ищу, чтобы кто-то закодировал это для меня; Я поставил там код, поэтому моя проблема понятна.

Мне нужна стратегия - способ атаковать это. Я знаю некоторые основные физики, но я не эксперт. Например, имеет ли эта проблема имя? Такого рода вещи.

Большое спасибо.

EDIT: Beta предоставила правильную стратегию для этого, и судья любезно внедрил его непосредственно в LÖVE в комментариях.

EDIT2: после большего количества поисковых запросов я также нашел openSteer. Это на С++, но он делает то, что я притворился. Вероятно, это будет полезно любому, кто достигнет этого вопроса.

Ответ 1

Он назвал планирование движения, и это не тривиально.

Здесь простой способ получить неоптимальную траекторию:

  • Стоп. Прикладывайте упор против направления скорости до тех пор, пока скорость не станет равной нулю.
  • Рассчитайте последнюю ногу, которая будет противоположна первой, устойчивый удар от стоячего старта, который получает корабль в x0 и v0. Начальная точка будет находиться на расстоянии | v0 | ^ 2/(2 * тяга) от x0.
  • Доберитесь до этой начальной точки (а затем сделайте последнюю ногу). Переход из одной точки в другую легко: надавите на нее, пока вы не на полпути, а затем опуститесь назад, пока не остановитесь.

Если вам нужен быстрый и грязный подход к оптимальной траектории, вы можете использовать итеративный подход: Начните с неоптимального подхода, выше; что только временная последовательность углов тяги. Теперь попробуйте сделать небольшие вариации этой последовательности, сохранив популяцию последовательностей, которые приблизятся к цели. отвергайте худшее, экспериментируйте с лучшими - если вы чувствуете смелость, вы можете сделать это генетическим алгоритмом - и, к счастью, он начнет округлять углы.

Если вам нужен точный ответ, используйте вариационное исчисление. Я возьму треск, и если мне удастся, я отправлю ответ здесь.

EDIT: Здесь точное решение более простой задачи.

Предположим вместо тяги, которую мы можем указать в любом направлении, у нас есть четыре фиксированных движителя, указывающих в направлениях {+ X, + Y, -X, -Y}. В любой момент времени мы будем стрелять не более чем из одного из +/- X и не более одного из +/- Y (в стрельбе нет + x и -X одновременно). Итак, теперь проблемы X и Y независимы (они не находятся в исходной задаче, потому что нужно распределять нагрузку между X и Y). Теперь мы должны решить одномерную проблему - и применить ее дважды.

Оказывается, лучшая траектория включает в себя толкание в одном направлении, затем другое, а не возвращение к первому снова. (Побережье полезно, только если решение другой оси займет больше времени, чем у вас, чтобы у вас было время убить.) Сначала разрешите проблему скорости: предположим (WLOG), что ваша целевая скорость больше начальной скорости. Для достижения целевой скорости вам понадобится период тяги (+) длительности

T = (Vf - Vi)/a

(Я использую Vf: конечная скорость, Vi: начальная скорость, a: величина тяги.)

Мы замечаем, что если это все, что мы делаем, местоположение не получится. Фактическое конечное местоположение будет

X = (Vi + Vf)T/2

Итак, мы должны добавить исправление

D = Xf - X = Xf -(Vi+Vf)T/2

Теперь, чтобы местоположение получилось правильно, мы добавим период тяги в одном направлении до и равный период в противоположном направлении после. Это оставит конечную скорость невозмущенной, но даст нам некоторое смещение. Если длительность этого первого периода (и третьего) равна t, то смещение, которое мы получаем от него, составляет

d = +/-(at^2 + atT)

+/- зависит от того, нажимаем ли мы + затем -, или - затем+. Пусть это +. Мы решаем квадратичную форму:

t = (-aT + sqrt(a^2 T^2 + 4 a D))/2a

И все готово.

Ответ 2

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

  • " импульсы": сила [пользователь/программа].
    По-видимому, пользователь (или программа) имеет полный контроль над этим, т.е. Управляет направлением импульса и его тягой (возможно, в диапазоне от 0 до max).
  • некоторая внешняя сила: назовите это гравитацией, что угодно... Такая сила может быть обусловлена ​​несколькими источниками, но нас просто интересует полученная объединенная сила: в данный момент времени и в пространстве эта внешняя сила воздействует на корабль с заданной силой и направлением. Пользователь/программа не контролирует их.
  • инерция: это связано с текущей скоростью и направлением судна. Эта сила обычно заставляет корабль продолжать свое текущее направление с его текущей скоростью. Могут быть и другие параметры [космос-возраст], контролирующие инерцию, но в целом они пропорциональны скорости и массе судна. (Интуитивно, будет легче остановить корабль, если его текущая скорость меньше и/или если его масса меньше)

По-видимому, пользователь/программа только контролирует (в пределах) первую силу.
Непонятно, из вопроса, есть ли проблема:

  • [Проблема A], чтобы написать программу, которая обнаруживает динамику системы (и/или адаптируется к изменениям этой динамики).
    или..
  • [Проблема B] предложить модель-формулу, которая может быть использована для вычисления объединенной силы, которая в конечном итоге применяется к судну: "взвешенная" сумма управляемого пользователем импульса и двух других системных/физических силы.

Последний вопрос, проблема B, более легко и кратко излагается, поэтому предложите следующую модель:

Constant Parameters:
  ExternalForceX   = strength of the external force in the X direction
  ExternalForceY   = id. Y direction
  MassOfShip       = coeficient controlling 
Variable Parameters:
  ImpulseAngle     = direction of impulse
  ImpulseThrust    = force of thrust
Formula:
  Vx[new] = (cos(ImpulseAngle) * ImpulseThrust) + ExternalForceX  + (MassOfShip * Vx[current])
  Vy[new] = (sin(ImpulseAngle) * ImpulseThrust) + ExternalForceY  + (MassOfShip * Vy[current])

Обратите внимание, что приведенная выше модель предполагает постоянную внешнюю силу (постоянную как по силе, так и по направлению); то есть: аналогично гравитационному полю, относительно удаленному от отображаемой области (точно так же, как гравитация Земли, рассматриваемая в пределах футбольного поля). Если масштаб отображаемой области большой относительно источника (источников) внешних сил, тогда средний член приведенных выше формул должен быть изменен, чтобы включить: тригонометрический коэффициент, основанный на угле между центром источника и текущим положение и/или [обратный] коэффициент пропорциональности, основанный на расстоянии между центром источника и текущей позицией.
Точно так же предполагается, что масса судна остается постоянной, она может быть переменной, основанной на массе корабля, когда пустая, к которой вес топлива удаляется/добавляется по ходу игры.

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

Что, если вместо этого динамика системы была бы легко запрограммирована в игре (и считалась скрытой/случайной), и задача состоит в том, чтобы написать программу, которая будет постепенно определять направление и значение тяги импульсов для управления корабль к его целевому назначению, таким образом, чтобы его скорость на цели была как можно ближе к getTargetVelocity()? Это "Проблема А".

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

  • как далеко мы находим текущие значения из "заданного значения": это P = Пропорциональная часть PID
  • насколько быстро мы приближаемся к "заданной точке": это D = производная часть PID
  • как долго и сколько мы находимся вдали от "уставки": это я = интергральная часть PID

Менее сложный контроллер может, например, использовать только пропорциональный коэффициент. Это привело бы к колебаниям, иногда с большой амплитудой по обе стороны от уставки ( "Я на X единиц от того места, где я должен быть: позвольте мне выдернуть рулевое колесо и нажать на газ" ). Такое превышение заданного значения смягчается фактором производной ( "Да, я все еще не там, где должен быть, но прогресс, который я сделал с того момента, когда я последний раз проверял, очень большой: лучше замедлить" ), Наконец, интегральная часть учитывает тот факт, что все вещи, которые равны по отношению к объединенной пропорциональной и производной части, будут иметь меньшее или большее действие в зависимости от того, были ли мы "внедорожными" в течение длительного времени или нет и в значительной степени вне нас было все это время (например, "В последнее время мы отслеживали довольно близко к тому, где мы должны быть, не нужно делать опрометчивые ходы" )

Мы можем обсудить детали, реализующие ПИД-контроллеры для конкретных потребностей игры в космическом корабле, если это действительно то, что требуется. Идея заключалась в том, чтобы обеспечить вкус того, что можно сделать.

Ответ 3

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

-- shortest path minus initial velocity
dx,dy = x0 - x - vx, y0 - y - vy

-- normalize the direction vector
magnitude = sqrt(dx*dx + dy*dy)
dx,dy = dx/magnitude, dy/mangitude

-- apply the thrust in the direction we just calculated
self:applyImpulse(thrust*dx, thrust*dy)

Обратите внимание, что это не учитывает целевую скорость, потому что это становится чрезвычайно сложным.

У меня есть очень маленький модуль Lua для обработки 2D-векторов в этот паштет для вставки. Вы можете использовать его. Приведенный выше код уменьшится до:

d = destination - position - velocity
d:normalize()
d = d * thrust
self:applyImpulse(d.x, d.y)

Ответ 4

Вы изгоняете топливо? Ваша масса будет меняться со временем, если вы находитесь.

Thrust - это реактивная сила. Это скорость изменения массы, умноженная на скорость выхлопа относительно космического корабля.

Есть ли у вас внешние силы? Если вы это сделаете, им необходимо ввести свой импульсный расчет.

Предположим, что магическая тяга без вытеснения массы и внешних сил.

Импульс имеет единицы импульса. Это интеграл силы во времени.

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

Предполагая, что ваша "тяга" является силой, вы умножаете эту тягу на временной интервал (1/30 секунды), чтобы получить импульс, и вырвите компоненты.

Не знаю, отвечаю ли я на ваш вопрос, но, надеюсь, это поможет вам немного понять физику.

Ответ 5

Легче думать, если вы отделите скорость судна на компоненты, параллельные и перпендикулярные вектору скорости цели.

Рассматривая вдоль перпендикулярной оси, корабль хочет как можно скорее войти в позицию цели, а затем остаться там.

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

Я имел дело с двумя из них отдельно и, вероятно, сначала перпендикулярно. Как только он работает, и если это не будет достаточно хорошим, вы можете начать думать, есть ли способы заставить корабль стрелять интеллектуальными углами между перпендикулярными и параллельными.

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

Ответ 6

Ваш угол - это обратная касательная, противоположная/смежная

Итак, угол = InvTan (VY/VX)

Не уверен, что вы говорите о желании дрейфа?