Хорошо, я уже опубликовал это на math.stackechange.com, но не получил никаких ответов :(
Сначала приведу картину моей проблемы, описание следует потом:
Итак, я настроил все пункты и ценности.
Корабль начинает двигаться по левой планете P1
с S=0.27 Degrees
каждой игровой меткой, когда он достигает Point A
его, начинает следовать по кривой Безье до тех пор, пока не достигнет Point D
, затем он движется вокруг правой планеты P2
с тактом S=0.42 Degrees
за игру. Разница в S
том, что путешествуйте с одинаковой скоростью движения вокруг планет.
Пока все хорошо, у меня все получилось, теперь моя проблема.
Когда S P1
и S P2
сильно отличаются, корабль прыгает между двумя скоростями, когда он достигает пункта назначения, что выглядит довольно плохо. Поэтому мне нужно , чтобы ускорить корабль между Point A
и Point D
от S P1
к S P2
.
Вещи, которые я пропускаю, в фиолетовом, это:
Способ подсчета тиков, который требуется кораблю, чтобы двигаться по безье с учетом ускорения.
И способ найти положение на кривой Безье на основе T, опять же с учетом ускорения.
Я рассчитываю длину безье, рассчитывая расстояние между N
его точками. Так что я думаю, что мне нужно, это способ масштабирования того, что T
мне нужно включить в мои вычисления Безье, в соответствии с ускорением.
источник
Ответы:
Хорошо, у меня все работает, это заняло вечность, поэтому я опубликую свое подробное решение здесь.
Примечание: все примеры кода в JavaScript.
Итак, давайте разберем проблему на основные части:
Вам нужно вычислить длину, а также точки между
0..1
на кривой БезьеТеперь вам нужно настроить масштаб,
T
чтобы ускорить корабль с одной скорости до другойПравильно понять Безье
Найти некоторый код для рисования кривой Безье легко, хотя есть несколько различных подходов, один из них - алгоритм Де-Кастельхау , но вы также можете просто использовать уравнение для кубических кривых Безье:
С этим теперь можно нарисовать кривую Безье, вызвав
x
иy
сt
диапазонами0 to 1
, давайте посмотрим:Э-э ... это не совсем равномерное распределение очков, не так ли?
В связи с характером кривой Безье точки
0...1
имеют разные точкиarc lenghts
, поэтому сегменты вблизи начала и конца длиннее, чем те, которые находятся ближе к середине кривой.Равномерное отображение T на кривой AKA Параметризация длины дуги
Так что делать? Говоря простым языком, нам нужна функция для отображения нашего
T
наt
кривую, чтобы нашиT 0.25
результатыt
были на25%
длине кривой.Как мы это делаем? Ну, мы в Google ... но оказывается, что этот термин не так уж гугл , и в какой-то момент вы попадете в этот PDF . Это, безусловно, отличное чтение, но в случае, если вы уже забыли все математические вещи, которые вы изучали в школе (или вам просто не нравятся эти математические символы), это довольно бесполезно.
Что теперь? Ну, иди и Google еще немного (читай: 6 часов), и ты, наконец, найдешь отличную статью по этой теме (включая красивые картинки! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
Делать фактический код
Если вы просто не смогли устоять перед загрузкой этих PDF, хотя вы уже давно потеряли свои математические знания (и вам удалось пропустить великолепную ссылку на статью), вы можете подумать: «Боже, это займет сотни строк кода и тонны процессора »
Нет, не будет. Потому что мы делаем то, что делают все программисты, когда дело касается математики:
мы просто обманываем.
Параметрирование длины дуги, ленивый путь
Посмотрим правде в глаза, нам не нужна бесконечная точность в нашей игре, не так ли? Поэтому, если вы не работаете в Nasa и не планируете отправлять людей на Марс, вам не понадобится
0.000001 pixel
идеальное решение.Так, как мы наносим
T
на картуt
? Это просто и состоит только из 3 шагов:Вычислите
N
точки на кривой, используяt
и сохранитеarc-length
(или длину кривой) в этой позиции в массивЧтобы отобразить
T
наt
первую умножитьT
на общую длину кривой , чтобы получить ,u
а затем искать массив длин для индекса наибольшего значения , которое меньшеu
Если у нас был точный результат, вернуть значение массива по этому индексу, разделенное
N
, если не интерполировать немного между найденной точкой и следующей, разделить вещь еще разN
и вернуть.Вот и все! Итак, теперь давайте посмотрим на полный код:
Это инициализирует нашу новую кривую и вычисляет
arg-lenghts
, а также сохраняет последнюю из длин вtotal length
качестве кривой, ключевым фактором здесьthis.len
является нашаN
. Чем выше, тем более точным будет сопоставление, поскольку кривой размера на рисунке выше100 points
кажется достаточным, если вам просто нужна хорошая оценка длины, что-то вроде25
этого уже выполнит свою работу, когда у нас всего 1 пиксель. Например, но тогда у вас будет менее точное отображение, что приведет к неравномерному распределениюT
при сопоставленииt
.Фактический код отображения: сначала мы делаем простую
binary search
с нашими сохраненными длинами, чтобы найти наибольшую длину, которая меньшеtargetLength
, затем мы просто возвращаем или выполняем интерполяцию и возвращаем.Опять же, это вычисляется
t
на кривой.Время для результатов
Теперь, используя
mx
иmy
вы получите равномерное распределениеT
по кривой :)Разве это не сложно? Еще раз, оказывается, что простого (хотя и не идеального решения) будет достаточно для игры.
Если вы хотите увидеть полный код, есть доступный Gist:
https://gist.github.com/670236
Наконец, ускорение кораблей
Таким образом, все, что осталось сейчас, - это ускорить корабли вдоль их пути, отображая положение, в
T
котором мы затем используем их для нахожденияt
на нашей кривой.Сначала нам нужно два уравнения движения , а именно
ut + 1/2at²
и(v - u) / t
В реальном коде это будет выглядеть так:
Затем мы уменьшаем это до
0...1
:И вот, корабли теперь плавно движутся по пути.
Если это не работает ...
Когда вы читаете это, все работает отлично и отлично, но у меня изначально были некоторые проблемы с ускорением, когда я объяснял проблему кому-то в чате gamedev, я обнаружил последнюю ошибку в своем мышлении.
Если вы еще не забыли про картинку в исходном вопросе, я упоминаю
s
там, оказывается, чтоs
это скорость в градусах , но корабли движутся по пути в пикселях, и я забыл об этом факте. Поэтому в этом случае мне нужно было преобразовать смещение в градусах в смещение в пикселях, и оказалось, что это довольно просто:Так вот и все! Спасибо за чтение ;)
источник
Проблема в том, что судно не пойдет по этой траектории естественным путем. Таким образом, даже если он работает отлично, он все равно не будет выглядеть правильно.
Если вы хотите смоделировать плавный переход между планетами, я бы предложил на самом деле его моделировать. Уравнения очень просты, поскольку у вас есть только две значительные силы: сила тяжести и тяга.
Вам просто нужно установить свои константы: масса P1, P2, корабль
С каждым тактом игры (время: t) вы делаете 3 вещи
Рассчитайте гравитацию p1 на корабле и p2 на корабле, добавьте результирующие векторы к вектору тяги.
Рассчитайте вашу новую скорость на основе вашего нового ускорения с шага 1
Переместить корабль в соответствии с вашей новой скоростью
Это может показаться большой работой, но это может быть сделано в дюжине строк кода и будет выглядеть очень естественно.
Если вам нужна помощь с физикой, дайте мне знать.
источник
t
:)Я нашел отличную статью, объясняющую возможное решение этой проблемы с помощью примера кода, написанного на javascript. Он работает, «подталкивая» значение t в правильном направлении.
На этот вопрос уже есть много интересных ответов, но я нашел, что это решение стоит отметить.
источник
Спасибо за отличную страницу с описанием того, как вы решили эту проблему. Я сделал что-то несколько отличное от вас в одной детали, так как у меня были большие ограничения памяти: я не строю массив или не ищу его для правильного «сегмента» с помощью двоичного поиска. Это потому, что я всегда знаю, что перехожу с одного конца моей кривой Безье на другой: поэтому я просто запоминаю «текущий» сегмент, и если я увижу, что я собираюсь выйти за пределы этого сегмента, чтобы вычислить мой следующий положение, я вычисляю следующий (или предыдущий) сегмент (в зависимости от направления движения). Это хорошо работает для моего приложения. Единственный сбой, который мне пришлось решить, заключался в том, что на некоторых кривых размер сегментов был настолько мал, что мой следующий график, который нужно было указать, был в редких случаях более чем на один сегмент впереди текущего, поэтому вместо того, чтобы просто идти к
Не знаю, имеет ли это смысл, но это, безусловно, помогло мне.
источник
Такое моделирование странно и может привести к странным нелогичным результатам. Особенно, если скорость стартовых планет очень мала.
Моделируйте корабли с силой тяги.
Когда корабли находятся на последней орбите на стартовой планете, ускоряйтесь с полным толчком.
Когда корабль достигает определенного расстояния, используйте обратную тягу, чтобы замедлить корабль до скорости орбиты целевой планеты.
Изменить: Выполните всю симуляцию сразу, когда узел собирается покинуть орбиту. либо отправьте все данные, либо отправьте только несколько векторов движения с интервалами и выполните интерполяцию между ними.
источник
Если я правильно понимаю, ваша проблема слишком ограничена.
Я полагаю, что вы хотите, чтобы космический корабль путешествовал по заданному пути между орбитами в течение некоторого времени t , и вы также хотите, чтобы он ускорялся со скорости s1 до скорости s2 в то же время t . К сожалению, вы не можете (в общем) найти ускорение, которое удовлетворяет обоим этим ограничениям одновременно.
Вам нужно немного ослабить свою проблему, чтобы она разрешилась.
источник
Я столкнулся с этим ответом, потому что я стремлюсь распределить точки равномерно вдоль пути SVG, который использует кривую Безье.
Несмотря на то, что MDN утверждает, что это устарело, вы можете использовать
path.getPointAtLength
правильный результат. https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLengthВ настоящее время он работает в Chrome / Safari / Firefox и должен работать в IE / Edge, но я не проверял эти 2.
источник
Проблема с принятым решением
Поскольку Безье является экспоненциальной функцией, мы ожидаем, что в разных областях кривой будут действовать разные темпы продвижения.
Поскольку решение Ivo линейно интерполирует между этими начальными экспоненциальными выборками, неточности будут сильно смещены к концам / середине (обычно кубической) кривой, где эти дельты самые большие; поэтому, если частота выборки не
N
будет значительно увеличена, как он предполагает, ошибки будут очевидны, и при некотором уровне масштабирования всегда будут очевидными для данногоN
, то есть смещение является внутренним для этого алгоритма. Не подходит, например, для векторной графики, где масштабирование может быть неограниченным.Противодействие смещению посредством управляемого отбора
Альтернативным решением является линейно переназначить ,
distance
чтобыt
после того, как противодействие естественный уклон , что функция Безье производит.Предполагая, что это то, что мы в идеале хотим:
но это то, что мы получаем из функции положения Безье:
Посмотрев на
N
взятые выборки, мы можем увидеть, где дельты расстояния являются самыми большими, и пересчитать («разделить») на полпути между двумя соседними расстояниями, увеличившисьN
на 1. Например, разделив наt=0.9
(который находится на полпути в самой большой дельте), мы могли бы получить:Мы повторяем этот процесс для следующего наибольшего интервала расстояний до тех пор, пока максимальная дельта между любыми двумя расстояниями во всем наборе не станет ниже некоторой
minDistanceDelta
, а более конкретно, меньше, чем наepsilon
расстоянии от определенных расстояний, которые мы хотим отобразить на шагахt
; тогда мы можем линейно отобразить наши желаемыеt
шаги на соответствующиеdistance
s. Это создает хеш-таблицу / карту, к которой вы можете получить дешевый доступ и значения которой вы можете перемещать между во время выполнения, без смещения.По мере роста набора
N
стоимость повторения увеличивается, поэтому в идеале это нужно сделать как предварительный процесс. Каждый разN
, когда увеличивается, добавьте два новых результирующих интервала вintervals
коллекцию, удаляя старый, единственный интервал, который они заменили. Это структура, над которой вы работаете, чтобы найти следующий наибольший интервал, который нужно разделить на две части. Сортировкаintervals
по расстоянию облегчает задачу, так как вы можете просто вставить следующий рабочий элемент в конец, разделить и т. Д.В итоге получается что-то вроде того, что мы в идеале хотели:
Так как мы принимаем догадки на каждом шаге, мы не получим точные точные расстояния
2
и4
т. Д. , Которые мы хотели, но с помощью повторной итерации они достаточно приблизятся к желаемым значениям расстояний, поэтому вы сможетеt
с достаточной точностью отобразить ваши шаги, устраняя смещение из-за почти равноотстоящей выборке.Затем вы можете получить, например
t=0.5
, как это делает Иво в своем ответе, т.е. путем интерполяции между двумя ближайшими значениями выше (3.9998132
и6.00703
).Заключение
В большинстве случаев решение Ivo будет работать хорошо, но в случаях, когда нужно избегать смещения любой ценой, убедитесь, что ваши
distance
s как можно более равномерно распределены, а затем линейно сопоставленыt
.Обратите внимание, что разделение может быть выполнено стохастически, а не делением по середине каждый раз, например, мы могли бы разделить этот первый примерный интервал на,
t=0.827
а не наt=0.9
.источник