Как я могу перехватить объект с круговым движением

23

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

Игра не является научно точной, поэтому меня не волнуют инерция, гравитация, эллиптические орбиты и т. Д.

Я знаю местоположение и скорость космических кораблей, а также орбиту и скорость планет

введите описание изображения здесь

Ausa
источник
1
Нет, я пытаюсь вычислить угол, на который корабль должен двигаться, чтобы перехватить планету.
Ауса
4
Это, вероятно, будет работать лучше в math.stackexchange.com ..
Яри ​​Комппа
2
Может ли ваш корабль изменять скорость и направление или они постоянны? Кроме того, этот вопрос о том, как избежать попадания ракет в цель, может быть полезным.
thegrinner
4
Чтобы уточнить, это ситуация? дано для планеты: центр орбиты, радиус орбиты, угловая скорость, текущее местоположение; для корабля : текущее местоположение, текущая скорость; определить направление движения корабля, чтобы перехватить планету
AakashM
6
Как интересное историческое примечание: планеты обычно вращаются в том же направлении, что и их орбита, которая, следовательно, также против часовой стрелки, как видно из северного полушария. Из этого факта мы можем сделать вывод, что солнечные часы были изобретены в северном полушарии . Если бы солнечные часы были изобретены в южном полушарии, то по часовой стрелке был бы другой путь.
Эрик Липперт

Ответы:

3

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

Корабль может достичь ближайшей точки на орбите за время t_min :

shipOrbitRadius = (ship.position - planet.orbitCenter).length;
shortestDistance = abs(shipOrbitRadius - planet.orbitRadius);
t_min = shortestDistance/ship.maxSpeed;

Корабль может достичь ЛЮБОЙ точки на орбите за время, меньшее или равное t_max :

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

if(shipOrbitRadius > planet.orbitRadius)
{
   t_max = planet.orbitRadius * 2/ship.maxSpeed + t_min;
}
else
{
   t_max = planet.orbitRadius * 2/ship.maxSpeed - t_min;
}

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

Теперь мы можем использовать бинарный поиск между этими крайностями, t_min и t_max . Мы будем искать t-значение, которое возвращает ошибку, близкую к нулю:

error = (planet.positionAtTime(t) - ship.position).squareMagnitude/(ship.maxSpeed*ship.maxSpeed) - t*t;

(Используя эту конструкцию, error @ t_min> = 0 и error @ t_max <= 0, поэтому должен быть хотя бы один перехват с ошибкой = 0 для промежуточного значения t)

где для полноты функция положения что-то вроде ...

Vector2 Planet.positionAtTime(float t)
{
  angle = atan2(startPosition - orbitCenter) + t * orbitalSpeedInRadians;
  return new Vector2(cos(angle), sin(angle)) * orbitRadius + orbitCenter;
}

Обратите внимание, что если орбитальный период планеты очень короткий по сравнению со скоростью корабля, эта функция ошибок может несколько раз менять знаки на протяжении промежутка от t_min до t_max. Просто следите за самой ранней парой + ve & -ve, с которой вы столкнулись, и продолжайте поиск между ними, пока ошибка не станет достаточно близкой к нулю («достаточно близко», чувствительная к вашим юнитам и контексту игрового процесса. Квадрат половины длительности кадра может работать хорошо - это гарантирует, что перехват с точностью до кадра)

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

Вы всегда можете найти решение в итерациях Log_2 ((2 * orbitRadius / ship.maxSpeed) / errorThreshold). Так, например, если мой корабль может пересечь орбиту за 60 кадров, и мне нужен точный перехват с точностью до одного кадра, мне потребуется около 6 итераций.

Д.М.Григорий
источник
1
Здесь много хороших ответов, а также некоторые интересные альтернативные варианты, но из того, что у меня уже было, это решение выглядит лучше всего для моего случая. Я создал небольшую демонстрацию JavaScript моих результатов. Демонстрация
Ausa
11

Давайте не будем слишком усложнять это. Это не «идеальное» решение, но оно должно работать для большинства игр, и любые недостатки должны быть незаметны для игрока.

if(!OldTargetPoint)
  TargetPoint = PlanetPosition;
else
  TargetPoint = OldTargetPoint;
Distance = CurPosition - TargetPoint;
TimeNeeded = Distance / Speed;
TargetPoint = PlanetPositionInFuture(TimeNeeded);
SteerTowards(TargetPoint);
[...repeat this every AI update, for example every second...]
  1. Рассчитайте время, необходимое для достижения целевой точки.
  2. Рассчитайте, в каком положении будет находиться планета в расчетное время.
  3. Двигайтесь к расчетной точке.
  4. Повторение

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

Ошибка представляет собой разницу между вычисленным необходимым временем для достижения планеты (TimeNeeded) и фактическим временем, необходимым для достижения планеты (после учета новой TargetPoint).

API-Beast
источник
1
Возможно, вы захотите выполнить 2 итерации этого при запуске курса перехвата, в противном случае вы можете на мгновение увидеть, как корабль мигает между двумя направлениями (второе предположение может быть намного лучше, чем первое, и привести к совершенно другому курсу - особенно если корабль находится близко к орбите планеты или внутри
нее
1
@DMGregory Ой! Мы могли бы просто взять текущую позицию планеты вместо центра орбиты в качестве отправной точки. Когда мы рядом, это намного ближе, если мы далеко, это не имеет значения.
API-Beast
Также стоит отметить, что это работает лучше всего, когда планета движется медленно по сравнению с кораблем. Если скорость планеты сравнима или больше скорости корабля, вы можете увидеть колебания на пути корабля. При патологическом соотношении скорости корабль может вечно преследовать планету по концентрической орбите. Если ваши планеты быстры и вы заметили, что это происходит, вы можете заранее планировать весь курс перехвата, а не выполнять итерацию в полете.
DMGregory
3

Давайте начнем с рассмотрения математики, стоящей за этой проблемой.

Шаг 1:

Нахождение пересечения между линией и формой - это просто вопрос вставки уравнения линии в уравнение формы, которая в данном случае является кругом.

Линия, пересекающаяся с кругом

Возьмите круг с центром c и радиусом r . Точка p находится на окружности, если

|п-с|2знак равнор2

пзнак равноп0+μv

|п0+μv-с|2знак равнор2

Квадратное расстояние можно переписать в виде точечного произведения ( http://en.wikipedia.org/wiki/Dot_product ).

(p0+μvc)(p0+μvc)=r2

a=cp0(μv-a)(μv-a)знак равнор2

μ2(vv)-2μ(av)+aaзнак равнор2

|v|знак равно1

μ2-2μ(av)+|a|2-р2знак равно0

которое является простым квадратным уравнением, и мы приходим к решению

μзнак равноav+-sQрT((av)2*a2-р2)

μ<0

μзнак равно0

μ

Шаг 2:

μ

Что мы можем сделать с этим? Что ж, теперь мы знаем расстояние, которое должен пройти корабль, и в какой точке он окажется!

пзнак равноп0+μvμv

Теперь осталось только рассчитать, где должна быть планета, когда корабль начнет приближаться к своей орбите. Это легко рассчитать с помощью так называемых полярных каудинатов ( http://mathworld.wolfram.com/PolarCoordinates.html )

Иксзнак равнос+р*соs(θ)

Yзнак равнос+р*sяN(θ)

T*aNгULaрВеLосяTY

Резюме

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

Tholle
источник
8
Хороший анализ, но, похоже, он не отвечает на вопрос (поясненный в комментарии): «Нет, я пытаюсь вычислить угол, который должен двигаться корабль, чтобы перехватить планету». Вы берете угол корабля как данность и рассчитываете положение планеты, а не наоборот.
Chaposed0
4
Не буду опровергать это, потому что это полезный анализ, но я согласен с @ Chaposed0, что он не отвечает на вопрос. В своем резюме вы говорите «Выберите линию для своего корабля ...», но выбор этой линии - это как раз сложная часть.
Дрейк
1

Вот два «нестандартных» решения.

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

Решение первое: опровергнуть предпосылку вопроса. Количество, которое «скользит» в вопросе - это угол. Вместо этого исправьте это. Направьте корабль прямо в центр орбиты.

  • Рассчитайте положение, в котором корабль встретит планету; это легко.
  • Рассчитать расстояние от корабля до места перехвата; тоже легко.
  • Рассчитайте время, которое потребуется, пока планета не достигнет точки пересечения. Легко.
  • Разделите расстояние от корабля до перехвата на время, пока планета не дойдет до перехвата.
  • Если это меньше или равно максимальной скорости корабля, все готово. Установите корабль, движущийся с такой скоростью, прямо к солнцу.
  • В противном случае добавьте орбитальный период планеты ко времени и попробуйте снова. Продолжайте делать это, пока вы не наберете скорость, которая в пределах разумного для корабля.

Решение второе: вообще не делайте этого на автопилоте. Сделайте мини-игру, в которой игрок должен использовать двигатели, чтобы приблизиться к планете, и если они ударили по ней с слишком высокой относительной скоростью, они взорвутся, но у них также будет ограниченное топливо. Заставьте игрока научиться решать проблему перехвата!

Эрик Липперт
источник
1

(Икс,Y,T)

Tvзнак равноИкс2+Y2

v

Положение планеты в пространстве и времени может быть параметризовано, например,

Иксзнак равноИкс0+рсоs(весU+a)Yзнак равноY0+рsяN(весU+a)Tзнак равноU

U0весaU

Uvзнак равно(Икс0+рсоs(весU+a))2+(Y0+рsяN(весU+a))2U2v2знак равно(Икс0+рсоs(весU+a))2+(Y0+рsяN(весU+a))2U2v2знак равноИкс02+Y02+р2+2Икс0рсоs(весU+a)+2Y0рsяN(весU+a)

Это уравнение должно быть решено численно. У этого может быть много решений. Глядя на это, кажется, что всегда есть решение

Тони Макконен
источник
1

Вот часть решения. Я не успел закончить вовремя. Я попробую еще раз позже.

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

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

  • s_s = скорость корабля (s_s.x, s_s.y, также)
  • s_a= направление судна (угол движения, что мы хотим вычислить )
  • p_p = начальное положение планеты, глобальные координаты
  • p_r = расстояние планеты (радиус) от центра орбиты, полученное из p_p
  • p_a = начальный угол планеты в радианах относительно центра орбиты
  • p_s = угловая скорость планеты (рад / с)
  • t = время до столкновения (это тоже нужно вычислить)

Вот уравнения для положения двух тел, разбитых на составляющие:

ship.x = s_s.x * t * cos(s_a)
ship.y = s_s.y * t * sin(s_a)

planet.x = p_r * cos(p_a + p_s * t) + p_p.x
planet.y = p_r * sin(p_a + p_s * t) + p_p.y

Поскольку мы хотим ship.x = planet.xи ship.y = planet.yв какой-то момент tмы получаем это уравнение ( yслучай почти симметричный):

   s_s.x * t * cos(s_a) = p_r * cos(p_a + p_s * t) + p_p.x
   s_s.y * t * sin(s_a) = p_r * sin(p_a + p_s * t) + p_p.y

Решение главного уравнения для s_a:

   s_s.x * t * cos(s_a) = p_r * cos(p_a + p_s * t) + p_p.x
=> s_a = arccos((p_r * cos(p_a + p_s * t) + p_p.x) / (s_s.x * t))

Подстановка этого во второе уравнение приводит к довольно ужасающему уравнению, которое Wolfram alpha не решит для меня . Может быть лучший способ сделать это, не используя полярные координаты. Если кто-то захочет попробовать этот метод, добро пожаловать в него; Я сделал это вики. В противном случае, вы можете отнести это в Math StackExchange .

Chaposed0
источник
2
Я хотел бы, чтобы TeX был включен для этого сайта. Это сделало бы некоторые вещи, связанные с графикой (например, вектор, матрицы, кватернионы ...) проще для представления.
mvw
0

Я бы зафиксировал место, в котором нужно перехватить (выпустить круг на «исходящей» стороне орбиты.)

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

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

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

Затем ускорите или замедлите корабль, чтобы точно соответствовать отметке времени для этих N орбит.

Во всем этом фактический курс уже известен! Просто не скорость.

Брэм
источник
Это может привести к излишне длительным поездкам. Допустим, мы расположены так, что планета приближается к нам, и мы можем фактически достичь «приходящей» точки выпаса в то же время, что и планета. Если мы только смотрим на «исходящие» точки выпаса, то в итоге мы можем потратить еще полгода на транзит!
DMGregory
Правда ... зависит от орбитальных скоростей. Но это также минимизирует дельта-скорость, если вы всегда пасетесь на исходящих. При «входящем» вы можете сгореть в атмосфере, тогда как при «исходящем» вам, скорее всего, будет соответствовать. @DMGregory
Брэм