オブジェクトを別のオブジェクトの円周に沿って移動するにはどうすればよいですか?


11

私は数学が足りないので痛いですが、あなたにとってはこれは簡単なことでしょう。単純な円形のパス上で、オブジェクトを他のオブジェクトの年齢や周囲に沿って移動したい。現在のところ、私のゲームアルゴリズムは、スプライトを障害物の端に移動および配置する方法を知っており、さまざまな条件に応じて次のポイントが移動するのを待ちます。

したがって、ここでの数学的な問題は、中心(cX、cY)、オブジェクトの位置(oX、oY)、および移動に必要な距離(d)がわかっているときに、(aX、aY)および(bX、bY)の位置を取得する方法です。

ここに画像の説明を入力してください


1
あるd直線距離またはそれがアークのですか?
MichaelHouse

ピクセル単位の線形距離です
Lumis

ベクトルとは何か、およびそれらの基本的な操作については、よく知っていますか?
Patrick Hughes

@パトリックいいえ、ベクターに関するコースを受講する必要があると思います。これはフレームごとのアニメーションなので、コードは高速で最適化されている必要があります。
Lumis

回答:


8

警告:ここでは2つの近似を使用しています。1つ目はdを弧の長さとして、2つ目は直交長として取得します。これらの近似はどちらもdの値が比較的小さい場合に適していますが、これらは満たされません。コメントで明確にされた正確な質問)

幸いなことに、これに関する数学は比較的単純です。まず、中心位置から現在の位置までの相対ベクトルを見つけることができます。

deltaX = oX-cX;
deltaY = oY-cY;

そして、この相対ベクトルが得られたら、作業中の円の長さを知ることで、その半径を知ることができます。

radius = sqrt(deltaX*deltaX+deltaY*deltaY);

さらに、相対ベクトルから、cXからoXへの直線の正確な角度を見つけることができます。

curTheta = atan2(deltaX, deltaY);

今、物事は少しトリッキーになります。まず、円の円周、つまり角度が2πの円弧の「弧の長さ」は2πrであることを理解してください。一般に、半径rの円に沿った角度測定値がθの弧の弧長は、ちょうどθrです。ダイアグラムでdを弧の長さとして使用する場合、半径がわかっているので、分割するだけでシータの変化を見つけて新しい位置に移動できます。

deltaTheta = d/radius; // treats d as a distance along the arc

dを直線距離にする必要がある場合、状況は少し複雑になりますが、幸い、それほど多くはありません。そこでは、dは、他の2つの辺が円の半径である(それぞれcX / cYからoX / oYおよびaX / aYである)二等辺三角形の片側であり、この二等辺三角形を2等分すると、それぞれ2つの直角三角形が得られます。片側はd / 2、斜辺は半径です。これは、角度の半分の正弦が(d / 2)/半径であることを意味します。したがって、全角度はこれの2倍です。

deltaTheta = 2*asin(d/(2*radius)); // treats d as a linear distance

この式からアシンを取り、2をキャンセルした場合、これは最後の式と同じになることに注意してください。これは、sin(x)がxの小さな値の場合およそxであるということと同じであり、これは知っておくと便利な近似です。

これで、加算または減算するだけで新しい角度を見つけることができます。

newTheta = curTheta+deltaTheta; // This will take you to aX, aY. For bX/bY, use curTheta-deltaTheta

新しい角度を取得したら、基本的なトリガーを使用して、更新された相対ベクトルを見つけることができます。

newDeltaX = radius*cos(newTheta);
newDeltaY = radius*sin(newTheta);

そして、中心位置と相対ベクトルから(最終的に)ターゲットポイントを見つけることができます。

aX = cX+newDeltaX;
aY = cY+newDeltaY;

さて、これらすべてについて、いくつかの大きな注意点があります。まず、この数学はほとんどが浮動小数点であり、実際にはほぼ浮動小数点数であることがわかります。このメソッドを使用してループで更新し、すべてのステップで整数値に丸めると、円が閉じない(ループを回るたびに内側または外側に螺旋を描く)から最初に開始しないまで、すべてのことができます。場所!(dが小さすぎる場合、aX / aYまたはbX / bYの丸められたバージョンが、開始位置oX / oYがあった場所とまったく同じであることに気付く場合があります。)別の場合、これは、特に、行う; あなたのキャラクターが円弧状に移動されようとしている知っていれば、一般的には、事前に全体のアークを計画しなければならないではありませんここで最も高価な計算の多くをフロントローディングしてコストを削減できるため、このようにフレームごとにチェックしてください。本当にこのようにインクリメンタルに更新したい場合、コストを削減するもう1つの良い方法は、そもそもtrigを使用しないことです。dが小さく、正確である必要はないが非常に近い場合、中心に向かうベクトルに直交する長さdのベクトルをoX / oYに追加することにより、「トリック」を行うことができます(a (dX、dY)に直交するベクトルは(-dY、dX))で与えられ、それを正しい長さに縮小します。このコードについては、段階を追って説明することはしませんが、これまでに見てきたことを踏まえると、理解できると思います。最後のステップで新しいデルタベクトルを暗黙的に「縮小」することに注意してください。

deltaX = oX-cX; deltaY = oY-cY;
radius = sqrt(deltaX*deltaX+deltaY*deltaY);
orthoX = -deltaY*d/radius;
orthoY = deltaX*d/radius;
newDeltaX = deltaX+orthoX; newDeltaY = deltaY+orthoY;
newLength = sqrt(newDeltaX*newDeltaX+newDeltaY*newDeltaY);
aX = cX+newDeltaX*radius/newLength; aY = cY+newDeltaY*radius/newLength;

1
スティーブン正確さよりも自然で面白く感じることが重要なゲームにすぎないので、最初に近似を試してみると思います。速度も重要です。この長くて良いチュートリアルをありがとう!
Lumis

うわー、スティーブンあなたの近似は夢のように働いています!コードを変更してbX、bYを取得する方法を教えてください。あなたの直交する概念についてはまだはっきりしていません...
Lumis

2
承知しました!あなたは本当にある時点でベクトル数学を理解したいと思うでしょう、そしてあなたがいったん理解すれば、これは第二の性質になると思います。bX / bYを取得するには、「逆方向」に移動する必要があります。つまり、(特定の)直交ベクトルを追加するのではなく、減算するだけです。上記のコードでは、これは 'newDeltaX = deltaX-orthoX;になります。newDeltaY = deltaY-orthoY; '、newLengthの同じ計算、続いて' bX = cX + newDeltaX radius / newLength; bY = cY + newDeltaY radius / newLength; '。
Steven Stadnicki

基本的に、そのコードはnewDeltaX / newDeltaYを(aX / aYの方向ではなく)bX / bYの方向に向け、次にaX / aYを取得するのと同じようにトリミングして中央に追加します。
Steven Stadnicki

9

既に持っている2つの辺(1つの辺は 'c'から 'o'、もう1つの辺は 'o'から 'a')を使用して三角形を形成し、3番目の辺は 'a'から 'c'に向かいます。「a」がどこにあるかはまだわかりませんが、今のところそこにポイントがあると想像してください。側面「d」の反対側の角度の角度を計算するには、三角法が必要です。両方とも円の半径であるため、辺の長さはc <-> oとc <-> aです。

この三角形の3つの辺の長さをまだ確認できないので、三角形の「d」側と反対の角度を決定できます。必要な場合のSSS(サイドサイドサイド)式は次のとおりです。http//www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

SSS式を使用すると、側面「d」の反対側の角度(「j」と呼びます)が得られます。これで、(aX、aY)を計算できます。

// This is the angle from 'c' to 'o'
float angle = Math.atan2(oY - cY, oX - cX)

// Add the angle we calculated earlier.
angle += j;

Vector2 a = new Vector2( radius * Math.cos(angle), radius * Math.sin(angle) );

計算する角度が常にラジアンであることを確認してください。

円の半径を計算する必要がある場合は、ベクトル減算を使用して、ポイント 'o'からポイント 'c'を減算し、結果のベクトルの長さを取得できます。

float lengthSquared = ( inVect.x * inVect.x
                      + inVect.y * inVect.y
                      + inVect.z * inVect.z );

float radius = Math.sqrt(lengthSquared);

このようなことでうまくいくと思います。私はJavaを知らないので、正確な構文を推測しました。

Byte56この三角形がどのように見えるかを示すためにユーザーが指定した画像は次のとおりです。 カオトライアングル


1
私は答えを出していましたが、これで終わりです。私が作成した画像を使用してください:) i.imgur.com/UUBgM.png
MichaelHouse

@ Byte56:ありがとう、説明するための画像エディターハンドルはありませんでした。
ニックフォスター

半径も計算する必要があることに注意してください。二等辺三角形があるので、完全なSSS計算よりもjを取得する簡単な方法があるはずです。)
Steven Stadnicki

はい、それは私にとっても簡単に思えます!AndroidにはVector2がないため、値を個別に使用できると思います。Intrestingly私はここで、手動でのAndroid用に作成されたベクトル2クラスが見つかりました:code.google.com/p/beginning-android-games-2/source/browse/trunk/...
Lumis

(私は正しい線形距離を見つけるために自分の答えを微調整しました-2 * asin(d /(2 * radius))としてのdeltaThetaの2番目の計算は、ここでjを見つける方法です。)
Steven Stadnicki

2

obj1を中心にobj2を回転させるには、以下を試してください。

float angle = 0; //init angle

//call in an update
obj2.x = (obj1.x -= r*cos(angle));
obj2.y = (obj1.y += r*sin(angle));
angle-=0.5;

これはそもそも角度を取得する方法を示しておらず、質問のように座標を見つけるのではなく、軌道を設定する方法を示しているように見えます。
MichaelHouse

1
ルイス、ある点を中心にオブジェクトを周回させる方法を示してくれてありがとう。役に立つかもしれません...
Lumis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.