OK、私はすべてが機能している、それは永遠にかかったので、ここで私の詳細なソリューションを投稿するつもりです。
注:すべてのコードサンプルはJavaScriptで記述されています。
それでは、問題を基本的な部分に分けましょう。
0..1
ベジエ曲線の長さと、その間の点を計算する必要があります
T
船をある速度から別の速度に加速するために、スケーリングを調整する必要があります
ベジエを正しくする
ベジェ曲線を描くためのコードを見つけるのは簡単ですが、いくつかの異なるアプローチがありますが、そのうちの1つはDeCasteljauアルゴリズムですが、3次ベジェ曲線の方程式を使用することもできます。
// 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;
}
これにより、1は現在呼び出すことによって、ベジェ曲線を描くことができるx
とy
してt
の範囲これは0 to 1
、のは、見てみましょう:
ええと...それは実際にはポイントの均等な分布ではありませんか?
ベジエ曲線の性質により、上の点0...1
は異なりますarc lenghts
。そのため、開始点と終了点に近いセグメントは、曲線の中央に近いセグメントよりも長くなります。
Tを曲線上に均等にマッピングする、別名、アーク長パラメータ化
じゃあ何をすればいいの?まあ簡単な言葉で私たちはをマッピングする機能を必要とするT
上にt
、曲線のように、私たちT 0.25
に結果t
でのその25%
曲線の長さの。
それをどうやってやるの?私たちはグーグル...しかし、用語はグーグルほどではないことが判明し、ある時点でこのPDFをヒットします。確かに素晴らしい読み物ですが、学校で学んだすべての数学のことを忘れてしまった場合(または数学記号が嫌いな場合)は、ほとんど役に立たないでしょう。
今何?さあ、グーグルでもう少し(読んで:6時間)、ついにそのトピックに関する素晴らしい記事(素敵な写真を含む!^ _ ^ ")を見つけることができます:http :
//www.planetclegg.com/projects/WarpingTextToSplines.html
実際のコードを実行する
ケースであなただけのPDFのことをダウンロードする抵抗することができませんでしたが、すでに長い、長い時間前にあなたの数学の知識を失ったと思います(とあなたはスキップするように管理さ偉大な「神は、これは取る:記事リンク)を、あなたが今考えるかもしれません数百行のコードと大量のCPU」
いいえ、それはしません。数学のことになると、すべてのプログラマーが行うことを行うから
です。
アーク長パラメーター化、怠laな方法
それに直面してみましょう、私たちはゲームで無限の精度を必要としませんよね?したがって、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
は、例ではありますが、マッピングの精度は低く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で利用できるGistがあります
。
最後に、船を加速する
したがって、残されているのは、曲線上の位置T
を見つけるためt
に使用する位置をマッピングすることにより、船をその経路に沿って加速することです。
まず、運動方程式の 2つ、つまり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チャットルームの誰かに問題を説明するときに、加速部分にいくつかの問題が最初にありました。
元の質問の写真をまだ忘れていない場合は、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);
};
それですべてです!読んでくれてありがとう ;)