Перемещение кораблей между двумя планетами вдоль безье, отсутствие некоторых уравнений для ускорения

Хорошо, я уже разместил это на math.stackechange.com, но не получил никаких ответов: (

Прежде всего, это изображение моей проблемы, описание следует после:

alt text

Итак, я установил все точки и значения.

Корабль начинает перемещаться по левой планете 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, снова учитывая ускорение.

ATM. Я вычисляю длину безье, вычисляя расстояние между N своих точек. Итак, что мне нужно думать , это способ масштабирования T Мне нужно внести в мой расчет безье соответственно ускорению.

45 голосов | спросил Ivo Wetzel 9 22010vEurope/Moscow11bEurope/MoscowTue, 09 Nov 2010 19:52:48 +0300 2010, 19:52:48

5 ответов


68

ОК, у меня все работает, это навсегда, поэтому я собираюсь опубликовать здесь свое подробное решение.
Примечание. Все образцы кода находятся в JavaScript.

Итак, давайте разложим проблему на основные части:

  1. Вам нужно вычислить длину, а также точки между 0..1 на кривой безье

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

Получение права Безье

Найти код для рисования кривой Безье легко, существует ряд разных подходов, хотя один из них - Алгоритм DeCasteljau , но вы также можете просто использовать уравнение для кубического BÃ © zier кривые:

// Part of a class, a, b, c, d are the four control points of the curve
x: function (t) {
    return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
           + 3 * ((1 - t) * (1 - t)) * t * this.b.x
           + 3 * (1 - t) * (t * t) * this.c.x
           + (t * t * t) * this.d.x;
},

y: function (t) {
    return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
           + 3 * ((1 - t) * (1 - t)) * t * this.b.y
           + 3 * (1 - t) * (t * t) * this.c.y
           + (t * t * t) * this.d.y;
}

С помощью этого теперь можно рисовать беззерновую кривую, вызывая x и y с помощью t, который варьируется от 0 to 1, давайте посмотрим:

alt text

Э-э ... на самом деле это не равномерное распределение очков, не так ли? Из-за характера кривой BÃ © zier точки на 0...1 имеют разные длины arc lenghts, поэтому сегменты ближе к началу и концу длиннее, чем те, которые находятся вблизи середины кривой.

Отображение T равномерно по параметру дуги длины кривой AKA

Так что делать? Ну, просто, нам нужна функция для отображения нашего T на t кривой, так что наш T 0.25 приводит к t, который находится в 25% длины кривой.

Как мы это делаем? Ну, мы Google ... но, оказывается, этот термин не соответствует googleable , и в какой-то момент вы попадете в этот PDF . Который конечно замечательный, но в случае, если вы уже забыли все математические материалы, которые вы узнали в школе (или вам просто не нравятся эти математические символы), это довольно бесполезно.

Что теперь? Хорошо, и Google еще немного (читайте: 6 часов), и вы наконец найдете статью большой по этой теме (включая красивые фотографии! ^ _ ^ "):
http://www.planetclegg.com/projects/WarpingTextToSplines.html

Выполнение фактического кода

Если вы просто не могли удержаться от загрузки этого файла хотя , вы уже давно потеряли свои математические знания (и вам удалось пропустить great ссылка на статью), вы можете теперь подумать: «Боже, это займет сотни строк кода и тонны процессора»

Нет, не будет. Поскольку мы делаем то, что делают все программисты, когда дело доходит до математики:
Мы просто обманываем.

Параметризация длины дуги, ленивый способ

Посмотрим правде в глаза, нам не нужна бесконечная точность в нашей игре, не так ли? Поэтому, если вы не работаете в НАСА и не планируете отправлять людей на Марс, вам не понадобится идеальное решение 0.000001 pixel.

Итак, как мы отображаем T на t? Это просто и состоит всего из трех шагов:

  1. Рассчитайте N точки на кривой с помощью t и сохранитеarc-length (или длина кривой) в этой позиции в массив

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

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

Вот и все! Итак, теперь давайте посмотрим на полный код:

function Bezier(a, b, c, d) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;

    this.len = 100;
    this.arcLengths = new Array(this.len + 1);
    this.arcLengths[0] = 0;

    var ox = this.x(0), oy = this.y(0), clen = 0;
    for(var i = 1; i <= this.len; i += 1) {
        var x = this.x(i * 0.05), y = this.y(i * 0.05);
        var dx = ox - x, dy = oy - y;        
        clen += Math.sqrt(dx * dx + dy * dy);
        this.arcLengths[i] = clen;
        ox = x, oy = y;
    }
    this.length = clen;    
}

Это инициализирует нашу новую кривую и вычисляет arg-lenghts, она также сохраняет последнюю длину в виде total length кривой, ключевым фактором здесь является this.len, который является нашим this.len. Чем выше, тем точнее будет отображение, так как кривая размера на картинке выше N кажется достаточной, если вам просто нужна хорошая оценка длины, что-то вроде 100 points уже выполнит задание с отображением всего 1 пикселя в нашем примере, но тогда у вас будет менее точное отображение, которое приведет к неравномерному распределению 25 при сопоставлении с T.

t

Фактический код преобразования, сначала мы делаем простой Bezier.prototype = { map: function(u) { var targetLength = u * this.arcLengths[this.len]; var low = 0, high = this.len, index = 0; while (low < high) { index = low + (((high - low) / 2) | 0); if (this.arcLengths[index] < targetLength) { low = index + 1; } else { high = index; } } if (this.arcLengths[index] > targetLength) { index--; } var lengthBefore = this.arcLengths[index]; if (lengthBefore === targetLength) { return index / this.len; } else { return (index + (targetLength - lengthBefore) / (this.arcLengths[index + 1] - lengthBefore)) / this.len; } }, mx: function (u) { return this.x(this.map(u)); }, my: function (u) { return this.y(this.map(u)); }, на нашей сохраненной длине, чтобы найти наибольшую длину, меньшую, чем binary search, тогда мы просто возвращаем или делаем интерполяция и возврат.

targetLength

Снова это вычисляет x: function (t) { return ((1 - t) * (1 - t) * (1 - t)) * this.a.x + 3 * ((1 - t) * (1 - t)) * t * this.b.x + 3 * (1 - t) * (t * t) * this.c.x + (t * t * t) * this.d.x; }, y: function (t) { return ((1 - t) * (1 - t) * (1 - t)) * this.a.y + 3 * ((1 - t) * (1 - t)) * t * this.b.y + 3 * (1 - t) * (t * t) * this.c.y + (t * t * t) * this.d.y; } }; на кривой.

Время для результатов

alt text

Теперь, используя t и mx, вы получите равномерно распределенный my на кривой:)

Разве это не так сложно, не так ли? Опять же, оказывается, что для игры достаточно простого (хотя и не идеального решения).

Если вы хотите увидеть полный код,существует Gist:
https://gist.github.com/670236

Наконец, ускорение кораблей

Итак, теперь все, что осталось, - ускорить корабли вдоль их пути, сопоставляя позицию на T, которую мы затем используем, чтобы найти T на нашей кривой.

Сначала нам нужны два из уравнений движения , а именно t и ut + 1/2at²

В текущем коде, который будет выглядеть так:

(v - u) / t

Затем мы масштабируем до startSpeed = getStartingSpeedInPixels() // Note: pixels endSpeed = getFinalSpeedInPixels() // Note: pixels acceleration = (endSpeed - startSpeed) // since we scale to 0...1 we can leave out the division by 1 here position = 0.5 * acceleration * t * t + startSpeed * t; , делая:

0...1

И вот, вы идете, корабли теперь плавно двигаются вдоль пути.

Если он не работает ...

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

Если вы уже не забыли об изображении в исходном вопросе, я упоминаю maxPosition = 0.5 * acceleration + startSpeed; newT = 1 / maxPosition * position; , получается, что s имеет скорость в градусов , но корабли перемещаются по пути в пикселях , и я забыл об этом. Поэтому в этом случае мне нужно было преобразовать смещение в градусах в смещение в пикселях, оказалось, что это довольно просто:

s

Вот и все! Спасибо за чтение;)

ответил Ivo Wetzel 11 42010vEurope/Moscow11bEurope/MoscowThu, 11 Nov 2010 03:20:36 +0300 2010, 03:20:36
4

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

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

Вам просто нужно установить свои константы: Масса P1, P2, корабль

С каждой игрой-тиком (время: t) вы делаете 3 вещи

  1. Рассчитайте гравитацию p1 на корабле и p2 на судне, добавьте результирующие векторы к вектору тяги.

  2. Рассчитайте свою новую скорость на основе вашего нового ускорения с шага 1

  3. Переместите корабль в соответствии с вашей новой скоростью

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

Если вам нужна помощь в физике, дайте мне знать.

ответил aaronfarr 11 42010vEurope/Moscow11bEurope/MoscowThu, 11 Nov 2010 18:49:57 +0300 2010, 18:49:57
0

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

Модель кораблей с тягой.

Когда корабли находятся на своей последней орбите на начальной планете, ускоряйтесь с полной тягой.

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

Изменить: выполнить весь симулятор сразу, когда узел собирается покинуть орбиту. либо отправить все данные, либо отправить несколько векторов движения с интервалами, и интерполировать между ними.

ответил AttackingHobo 9 22010vEurope/Moscow11bEurope/MoscowTue, 09 Nov 2010 21:45:03 +0300 2010, 21:45:03
0

Если я правильно понимаю, ваша проблема чрезмерно ограничена.

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

Вам нужно немного расслабиться, чтобы решить эту проблему.

ответил Gareth Rees 10 32010vEurope/Moscow11bEurope/MoscowWed, 10 Nov 2010 00:44:13 +0300 2010, 00:44:13
0

Спасибо за отличную страницу, описывающую, как вы решили эту проблему. Я делал что-то несколько отличное от вас в одной детали, так как я был сильно ограничен памяти: я не создавал массив или не искал его для «сегмента» с двоичным поиском. Это потому, что я всегда знаю, что я перехожу от одного конца кривой Безье к другому: поэтому я просто помню «текущий» сегмент, и если я увижу, что я собираюсь выйти за пределы этого сегмента, чтобы вычислить следующий позицию, я вычисляю следующий (или предыдущий) сегмент (в зависимости от направления движения). Это очень хорошо работает для моего приложения. Единственный сбой, который я должен был решить, состоял в том, что в некоторых кривых размер сегментов был настолько мал, что мой следующий сюжет указывал - в редкие времена - более одного сегмента впереди текущего, поэтому вместо того, чтобы просто идти к «следующему» или «предыдущему» сегменту (в зависимости от направления) мне приходилось вычислять сегменты вперед (или назад), пока не найду тот, что мой следующий пункт был «in».

Не знаю, действительно ли это имеет смысл, но это, безусловно, помогло мне.

ответил 18 42010vEurope/Moscow11bEurope/MoscowThu, 18 Nov 2010 22:12:38 +0300 2010, 22:12:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132