Гаразд, у мене все працює, це зайняло назавжди, тому я опублікую тут своє детальне рішення.
Примітка: Усі зразки коду знаходяться в JavaScript.
Тож давайте розбимо проблему на основні частини:
Вам потрібно обчислити довжину, а також точки між 0..1кривою Безьє
Тепер вам потрібно відрегулювати масштаб свого, Tщоб прискорити судно з однієї швидкості на іншу
Як правильно зрозуміти Безьє
Знайти якийсь код для малювання кривої Безьє легко, хоча існує декілька різних підходів, хоча один з них - алгоритм Декастелау , але ви також можете просто використовувати рівняння для кубічних кривих Безьє:
// 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, поглянемо:

Ага ... це насправді не рівномірний розподіл балів, чи не так?
Зважаючи на характер кривої Безьє, точки на них 0...1мають різні arc lenghts, тому відрізки біля початку та кінця довші, ніж ті, що знаходяться біля середини кривої.
Зображення T рівномірно на кривій параметризації довжини дуги AKA
То що робити? Ну в простих термінах нам потрібна функція для відображення OUR Tна tкривій, так що наші T 0.25результати в , tщо по крайней 25%довжини кривої.
Як ми це робимо? Ну, ми Google ... але виявляється, що цей термін не є таким, що можна шукати в 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і поверніть.
Це все! Тож тепер давайте розглянемо повний код:
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що є нашим N. Чим вище, тим точніше буде картографування, для кривої розміру на малюнку вище, 100 pointsздається, достатньо, якщо вам просто потрібна хороша оцінка довжини, щось подібне 25вже зробить роботу, будучи лише 1 пікселем у нашому Наприклад, але тоді у вас буде менш точне відображення, яке призведе до не настільки рівномірного розподілу, 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;
}
};
Знову це обчислюється tна кривій.
Час результатів

В даний час використовуючи, mxі myви отримуєте рівномірний розподіл Tна кривій :)
Хіба це не важко, чи не так? Ще раз виявляється, що для гри буде достатньо простого (хоча і не ідеального рішення).
У випадку, якщо ви хочете побачити повний код, вам доступна історія:
https://gist.github.com/670236
Нарешті, прискорення кораблів
Отже, все, що залишилося зараз, - це прискорити кораблі по їх шляху шляхом відображення положення, на 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, виконуючи:
maxPosition = 0.5 * acceleration + startSpeed;
newT = 1 / maxPosition * position;
А там ви йдете, кораблі тепер плавно рухаються по стежці.
У випадку, якщо це не працює ...
Коли ти це читаєш, все працює чудово і безглуздо, але я спочатку мав деякі проблеми з частиною прискорення, коли пояснював проблему комусь із чат-кімнати Гамедева, я виявив остаточну помилку в своєму мисленні.
Якщо ви вже не забули про картинку в оригінальному запитанні, я згадую sтам, виявляється, що sце швидкість у градусах , але кораблі рухаються по шляху в пікселях, і я забув про цей факт. Отже, що мені потрібно було зробити в цьому випадку - це перетворити зміщення в градусах в зміщення в пікселях, виявляється, що це досить просто:
function rotationToMovement(planetSize, rotationSpeed) {
var r = shipAngle * Math.PI / 180;
var rr = (shipAngle + rotationSpeed) * Math.PI / 180;
var orbit = planetSize + shipOrbit;
var dx = Math.cos(r) * orbit - Math.cos(rr) * orbit;
var dy = Math.sin(r) * orbit - Math.sin(rr) * orbit;
return Math.sqrt(dx * dx + dy * dy);
};
Так і це все! Дякуємо за прочитане;)