Tamam, her şey çalışıyor, sonsuza dek sürdü, bu yüzden ayrıntılı çözümümü buraya göndereceğim.
Not: Tüm kod örnekleri JavaScript’tedir.
Öyleyse sorunu temel parçalara ayıralım:
0..1
Bezier eğrisi arasındaki noktaların yanı sıra uzunluğunu da hesaplamanız gerekir.
Artık T
gemiyi bir hızdan diğerine hızlandırmak için ölçeklemenizi ayarlamanız gerekiyor.
Bezier'i Doğru Almak
Bezier eğrisi çizmek için bazı kodlar bulmak kolaydır, ancak bunlardan biri DeCasteljau Algoritması olmakla birlikte, birkaç farklı yaklaşım vardır , ancak aynı zamanda kübik Bézier eğrileri için de denklemi kullanabilirsiniz :
// 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;
}
Bununla, şimdi çağrı yaparak x
ve y
aralarında t
hangi aralıklarla bir bezier eğrisi çizebileceğimize 0 to 1
bakalım:
Uh ... bu gerçekten puanların eşit bir dağılımı değil, değil mi?
Bézier eğrisinin doğası gereği, noktalar 0...1
farklıdır arc lenghts
, bu nedenle başlangıç ve bitişe yakın bölümler, eğrinin ortasına yakın olanlardan daha uzundur.
AKA yay uzunluğu parametresi eğrisinde eşit olarak T eşlemesi
Peki ne yapmalı? Eh basit terimlerle Nerede biz eşlemek için bir işlev gerekir T
üzerine t
, eğrinin böylece bizim T 0.25
sonuçlanır t
de en o 25%
eğrisinin uzunluğu.
Bunu nasıl yaparız? Şey, biz Google ... ... ancak bu terimin bu kadar mantıklı olmadığı ve bir noktada bu PDF’ye çarpacağın ortaya çıktı . Hangi kesinlikle harika bir okuma, ancak okulda öğrendiğiniz tüm matematik şeylerini unutmuş olmanız durumunda (veya sadece bu matematiksel sembolleri sevmiyorsanız), bu oldukça işe yaramaz.
Şimdi ne var? Öyleyse git ve Google biraz daha oku (6 saat) ve sonunda konuyla ilgili harika bir makale buldun (güzel resimler dahil! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
Gerçek kodun yapılması
Eğer sadece PDF bulunuyor indirirken dayanamadı rağmen zaten uzun, çok uzun zaman önce senin matematik bilgisi kaybetmişler (ve atlamak için yönetilen büyük bir makale linki), şimdi düşünebilir: "Tanrı, bu alacak Yüzlerce satır kod ve CPU
Hayır, olmaz. Çünkü tüm programcıların yaptıklarını, matematik işlerine gelince yapıyoruz :
Basitçe hile yapıyoruz.
Yay uzunluğu parametresi, tembel yol
Kabul edelim, oyunumuzda sonsuz bir hassasiyete ihtiyacımız yok, değil mi? Öyleyse Nasa'da çalışıyorsanız ve insanlara Mars'ı göndermeyi planlamıyorsanız, 0.000001 pixel
mükemmel bir çözüme ihtiyacınız olmayacak .
Öyleyse nasıl harita oluşturuyorsunuz T
üzerine t
? Çok basit ve sadece 3 adımdan oluşuyor:
N
Eğri üzerindeki noktaları hesaplayın t
ve arc-length
(yani eğrinin uzunluğu) dizisini o konumda saklayın
Eşleştirmek için T
üzerine t
ilk çarpma, T
almak için eğrinin toplam uzunluğu ile u
ve daha sonra daha küçük büyük değer endeksi için uzunlukları dizi aramau
Kesin bir vuruş yaptıysak, bu indeks içindeki dizi değerini ikiye N
böldük, bulduğumuz nokta ile bir sonraki arasında bir bit arasına sokmazsanız, bir kez daha bölüp N
geri dönün.
Bu kadar! Şimdi tüm kodu inceleyelim:
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;
}
Bu yeni eğrimizi başlatır ve hesaplar, arg-lenghts
ayrıca uzunlukların sonunu total length
eğrinin sonu olarak kaydeder, burada this.len
bizim anahtar faktörümüzdür N
. Haritalama ne kadar yüksekse, haritalama o kadar kesin olacaktır, çünkü yukarıdaki resimdeki boyutun bir eğrisi 100 points
yeterli olacaktır, eğer sadece uzun bir tahminde bulunmanız gerekiyorsa, bizim gibi bir şey 25
zaten sadece 1 piksel kapalı olacak gibi yapacaktır. örnek, ama sonra bir o kadar da dağıtım sonuçlanacak bir az kesin eşleme olacak T
eşleştirilmiş zaman 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));
},
Gerçek haritalama kodu, önce binary search
daha küçük olan en büyük uzunluğu bulmak için depolanan uzunluklarımız üzerinde basit bir işlem yaparız targetLength
, sonra sadece enterpolasyon ve geri dönüş yaparız.
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;
}
};
Yine bu t
, eğri üzerinde hesaplar .
Sonuçların zamanı
Şimdi kullanarak mx
ve eğri üzerinde my
eşit bir şekilde dağılmış olsun T
:)
Zor değil miydi? Bir kez daha, basit (mükemmel bir çözüm olmasa da) bir oyun için yeterli olacağı ortaya çıktı.
Kodun tamamını görmek istiyorsanız, mevcut bir Gist vardır:
https://gist.github.com/670236
Sonunda gemileri hızlandırmak
Şimdi geriye kalan tek şey, gemileri yolunda hızlandırmak, T
daha sonra t
eğrimizi bulmak için kullandığımız pozisyonu haritalamak .
Öncelikle ihtiyacımız iki hareket denklemlerinin , yani ut + 1/2at²
ve(v - u) / t
Bu gibi görünen gerçek kodda:
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;
Sonra bunu 0...1
yaparak aşağı doğru ölçeklendiririz :
maxPosition = 0.5 * acceleration + startSpeed;
newT = 1 / maxPosition * position;
Ve işte gidiyorsunuz, gemiler şimdi yol boyunca sorunsuz hareket ediyor.
İşe yaramazsa ...
Bunu okurken, her şey yolunda ve zahmetli çalışıyor, ama başlangıçta ivme bölümüyle ilgili bazı problemler yaşadım, sorunu gamedev sohbet odasındaki birine açıklarken düşüncelerimdeki son hatayı buldum.
Asıl sorudaki resim hakkında henüz bir şey unutmadıysanız, s
orada belirtmek isterim ki, o dereces
hızdır , ancak gemiler yol boyunca pikseller halinde hareket ederler ve ben de bu gerçeği unuttum. Bu durumda yapmam gereken, yer değiştirmeyi derece cinsinden piksel cinsinden yer değiştirmeye dönüştürmekti, bunun oldukça kolay olduğu ortaya çıktı:
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);
};
Demek hepsi bu! Okuduğunuz için teşekkürler;)