ठीक है, मुझे सब कुछ काम मिल गया है, यह हमेशा के लिए हो गया है, इसलिए मैं यहां अपना विस्तृत समाधान पोस्ट करने जा रहा हूं।
नोट: सभी कोड नमूने जावास्क्रिप्ट में हैं।
तो चलो बुनियादी भागों में समस्या को तोड़ते हैं:
आपको लंबाई की गणना करने की आवश्यकता है, साथ ही 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 चाप-लंबाई परिमाणीकरण पर
इसलिए क्या करना है? अच्छी तरह से सरल शब्दों में, हमें वक्र T
पर अपना नक्शा बनाने के लिए एक फ़ंक्शन की आवश्यकता होती है t
, ताकि हमारे T 0.25
परिणाम t
उस पर हों25%
की लंबाई ।
हम यह कैसे करे? ठीक है, हम Google ... लेकिन यह पता चला है कि यह शब्द उस googleable नहीं है , और कुछ बिंदु पर आप इस पीडीएफ को मारेंगे । जो निश्चित रूप से एक महान पढ़ा है, लेकिन इस मामले में कि आप पहले से ही स्कूल में सीखे गए सभी गणित के सामान को भूल गए हैं (या आप सिर्फ उन गणितीय प्रतीकों को पसंद नहीं करते हैं) यह बहुत बेकार है।
अब क्या? ठीक है, और Google पर कुछ और पढ़ें (पढ़ें: 6 घंटे), और आपको अंत में विषय पर एक अच्छा लेख मिल जाएगा (अच्छी तस्वीर सहित! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
वास्तविक कोड करना
यदि आप अभी उस पीडीएफ को डाउनलोड करने का विरोध नहीं कर सकते हैं, हालांकि आप पहले से ही अपना गणितीय ज्ञान खो चुके हैं, बहुत समय पहले, (और आप महान लेख लिंक को छोड़ देने में कामयाब रहे ), तो अब आप सोच सकते हैं: "भगवान, यह लगेगा कोड और CPU की टन की सैकड़ों लाइनें "
नहीं यह नहीं होगा। क्योंकि हम वह सब करते हैं जो प्रोग्रामर करते हैं, जब गणित की सामग्री आती है:
हम बस धोखा देते हैं।
आर्क-लंबाई पैरामीटर, आलसी तरीका
चलो इसका सामना करते हैं, हमें अपने खेल में अंतहीन सटीकता की आवश्यकता नहीं है, क्या हम? इसलिए जब तक आप नासा में काम नहीं कर रहे हैं और लोगों को मंगल ग्रह पर भेजने की योजना बना रहे हैं, आपको एक 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
वक्र पर समान रूप से वितरित हो जाते हैं :)
क्या यह मुश्किल नहीं था? एक बार फिर, यह पता चलता है कि एक सरल (हालांकि सही समाधान नहीं) एक खेल के लिए पर्याप्त होगा।
यदि आप पूरा कोड देखना चाहते हैं, तो एक Gist उपलब्ध है:
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;
और वहाँ तुम जाओ, जहाज अब आसानी से पथ के साथ आगे बढ़ रहे हैं।
मामले में यह काम नहीं करता है ...
जब आप इसे पढ़ रहे होते हैं, तो सब कुछ ठीक और बांका काम करता है, लेकिन मुझे शुरू में त्वरण भाग के साथ कुछ समस्याएं थीं, जब किसी समस्या को समझाते हुए gamedev chatroom में मैंने अपनी सोच में अंतिम त्रुटि पाई।
यदि आप मूल प्रश्न में चित्र के बारे में पहले से ही नहीं भूले हैं, तो मैं 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);
};
तो और यह सब है! पढ़ने के लिए धन्यवाद ;)