OK, saya punya semuanya berfungsi, butuh selamanya, jadi saya akan memposting solusi rinci saya di sini.
Catatan: Semua sampel kode dalam JavaScript.
Jadi mari kita uraikan masalahnya menjadi beberapa bagian dasar:
Anda harus menghitung panjangnya, serta titik-titik di antara 0..1
kurva bezier
Anda sekarang perlu menyesuaikan skala Anda T
untuk mempercepat kapal dari satu kecepatan ke kecepatan lain
Memperbaiki Bezier dengan benar
Menemukan beberapa kode untuk menggambar kurva Bezier itu mudah, ada beberapa pendekatan yang berbeda, salah satunya adalah Algoritma DeCasteljau , tetapi Anda juga bisa menggunakan persamaan untuk kurva Bézier kubik:
// 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;
}
Dengan ini, satu sekarang dapat menggambar kurva bezier dengan menelepon x
dan y
dengan t
yang berkisar dari 0 to 1
, mari kita lihat:
Eh ... itu bukan benar-benar distribusi poin, kan?
Karena sifat kurva Bézier, titik-titik pada 0...1
memiliki perbedaan arc lenghts
, sehingga segmen di dekat awal dan akhir, lebih panjang daripada yang dekat dengan tengah kurva.
Memetakan T secara merata pada kurva parameterisasi panjang busur AKA
Jadi apa yang harus dilakukan? Nah secara sederhana kita membutuhkan fungsi untuk memetakan kita T
ke t
kurva, sehingga T 0.25
hasil t
kita berada 25%
pada panjang kurva.
Bagaimana kita melakukannya? Baiklah, kami Google ... tetapi ternyata istilah itu tidak dapat diunduh dari Google , dan pada titik tertentu Anda akan menemukan PDF ini . Yang pasti adalah bacaan yang bagus, tetapi jika Anda sudah lupa semua hal matematika yang Anda pelajari di sekolah (atau Anda tidak suka simbol-simbol matematika) itu sangat tidak berguna.
Apa sekarang? Baiklah dan Google lagi (baca: 6 jam), dan Anda akhirnya menemukan artikel yang bagus tentang topik tersebut (termasuk gambar yang bagus! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
Melakukan kode yang sebenarnya
Jika Anda tidak bisa menolak mengunduh PDF itu meskipun Anda sudah kehilangan pengetahuan matematis Anda sejak dulu, dan Anda berhasil melewati tautan artikel yang hebat , Anda mungkin sekarang berpikir: "Ya Tuhan, ini akan membutuhkan ratusan baris kode dan berton-ton CPU "
Tidak, tidak akan. Karena kami melakukan apa yang semua programmer lakukan, ketika sampai pada soal matematika:
Kami hanya curang.
Parameterisasi panjang busur, cara malas
Mari kita hadapi itu, kita tidak perlu ketelitian tanpa akhir dalam permainan kita, bukan? Jadi kecuali Anda bekerja di NASA dan berencana mengirim orang-orang Mars, Anda tidak akan membutuhkan 0.000001 pixel
solusi yang sempurna.
Jadi bagaimana kita memetakan T
ke t
? Ini sederhana dan hanya terdiri dari 3 langkah:
Hitung N
titik pada kurva menggunakan t
dan menyimpan arc-length
(alias panjang kurva) pada posisi itu ke dalam array
Untuk memetakan T
ke t
, yang pertama kali T
dengan total panjang kurva untuk mendapatkan u
dan kemudian mencari array panjang untuk indeks dari nilai terbesar yang lebih kecil dariu
Jika kami memiliki hit yang tepat, kembalikan nilai array pada indeks itu dibagi dengan N
, jika tidak menginterpolasi sedikit antara titik yang kami temukan dan yang berikutnya, bagi hal itu sekali lagi dengan N
dan kembali.
Itu saja! Jadi sekarang mari kita lihat kode lengkapnya:
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;
}
Ini menginisialisasi kurva baru kami dan menghitung arg-lenghts
, itu juga menyimpan yang terakhir dari panjang sebagai total length
kurva, faktor kunci di sini adalah this.len
yang kami N
. Semakin tinggi, semakin akurat pemetaannya, karena kurva ukuran pada gambar di atas 100 points
tampaknya sudah cukup, jika Anda hanya perlu perkiraan panjang yang baik, sesuatu seperti 25
sudah akan melakukan pekerjaan dengan hanya memiliki 1 pixel di contoh, tetapi kemudian Anda akan memiliki pemetaan yang kurang tepat yang akan menghasilkan distribusi tidak merata T
saat dipetakan ke 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));
},
Kode pemetaan yang sebenarnya, pertama-tama kita lakukan sederhana binary search
pada panjang yang disimpan untuk menemukan panjang terbesar yang lebih kecil targetLength
, lalu kita kembali atau melakukan interpolasi dan kembali.
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;
}
};
Sekali lagi ini menghitung t
pada kurva.
Waktunya untuk hasil
Dengan sekarang menggunakan mx
dan my
Anda mendapatkan merata T
pada kurva :)
Bukankah itu sulit, bukan? Sekali lagi, ternyata solusi sederhana (walaupun bukan solusi sempurna) akan cukup untuk sebuah gim.
Jika Anda ingin melihat kode lengkap, ada inti yang tersedia:
https://gist.github.com/670236
Akhirnya, mempercepat kapal
Jadi yang tersisa sekarang adalah mempercepat kapal di sepanjang jalurnya, dengan memetakan posisi T
yang akan kita gunakan untuk menemukan t
kurva kita.
Pertama kita perlu dua persamaan gerak , yaitu ut + 1/2at²
dan(v - u) / t
Dalam kode aktual yang akan terlihat seperti ini:
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;
Kemudian kami menurunkannya 0...1
dengan melakukan:
maxPosition = 0.5 * acceleration + startSpeed;
newT = 1 / maxPosition * position;
Dan begitulah, kapal sekarang bergerak dengan lancar di sepanjang jalan.
Jika tidak berfungsi ...
Ketika Anda membaca ini, semuanya bekerja dengan baik dan keren, tetapi saya awalnya memiliki beberapa masalah dengan bagian akselerasi, ketika menjelaskan masalah tersebut kepada seseorang di ruang obrolan gamedev, saya menemukan kesalahan terakhir dalam pemikiran saya.
Jika Anda belum lupa tentang gambar dalam pertanyaan asli, saya sebutkan di s
sana, ternyata itu s
adalah kecepatan dalam derajat , tetapi kapal-kapal bergerak di sepanjang jalan dalam piksel dan saya lupa tentang fakta itu. Jadi yang perlu saya lakukan dalam hal ini adalah mengubah perpindahan dalam derajat menjadi perpindahan dalam piksel, ternyata ini lebih mudah:
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);
};
Jadi itu saja! Terima kasih sudah membaca ;)