2つの2Dベクトル間の角度と適切な回転方向を計算するにはどうすればよいですか?


26

障害物がなく、動きがXY平面に制限されている動きAIに取り組んでいます。私は2つのベクトル、v、船1の対面方向、w、船1の位置から船2を指すベクトルを計算しています。

次に、式を使用してこれらの2つのベクトル間の角度を計算しています

arccos((v · w) / (|v| · |w|))

私が抱えている問題は、arccos0°から180°の間の値しか返さないことです。これにより、他の船と向き合うために左に曲がるか右に曲がるかを判断できなくなります。

これを行うためのより良い方法はありますか?


1
Unityを使用している場合は、チェックしてくださいMathf.DeltaAngle()
ラッセルボロゴーブ

回答:


22

2Dクロス積を使用する方がはるかに高速です。高価なトリガー機能は含まれていません。

b2Vec2 target( ... );
b2Vec2 heading( ... );

float cross = b2Cross( target, heading );

if( cross == -0.0f )
   // turn around

if( cross == 0.0f )
  // already traveling the right direction

if( cross < 0.0f)
  // turn left

if( cross > 0.0f)
  // turn right

それでも実際の角度が必要な場合は、を使用することをお勧めしatan2ます。 atan2任意のベクトルの絶対角度を提供します。任意のベクトルとベクトル間の相対角度を取得するには、絶対角度を計算し、単純な減算を使用します。

b2Vec2 A(...);
b2Vec2 B(...);

float angle_A = std::atan2(A.y,A.x);
float angle_B = B.GetAngle(); // Box2D already figured this out for you.

float angle_from_A_to_B = angle_B-angle_A;
float angle_from_B_to_A = angle_A-angle_B;

1
@Tetradの答えを読んだ後、外積とを組み合わせることができると思いますarccos。この方法では、トリガー関数を1つだけ使用しますが、実際の角度はそのままです。ただし、AI角度の追跡がゲームのパフォーマンスに顕著な影響を与えていることが確実になるまで、この最適化に反対することをお勧めします。
-deft_code

2
はい、ベクトルと角度を変換する場合、atan2()は間違いなくあなたの友人です。
bluescrn

1
ありがとう!私は実際に実際に角度を必要としないことがわかりました。2Dクロス積をつかむことは私のニーズに十分に簡単です。
エラー454

2
また、if( cross == -0.0f )vs if( cross == 0.0f )チェックは非常に脆弱です。
ボボボボ

1
@bobobobo、物理エンジンでは、方向を選択して移動するだけのオプションではない場合があります。魔法のように回転させると、物理エンジンがフリークします。さらに魔法のように回転させると、アニメーションでもひどく見えます。そのため、左または右の概念なしでこれを解決できますが、洗練されたソリューションではしばしばこれらが必要です。
-deft_code

10

2Dクロス積のアークサイン(クロス積ベクトルのzコンポーネント)を使用します。-90から90になり、左に進むか右に進むかがわかります。

AクロスBはBクロスAとは異なるため、注意してください。

別の戦略(ただし、それほど単純ではないかもしれません)は、atan2を使用して2つのベクトルの「ヘディング」を計算し、X度を指すAがY度を指すBに進むために左または右に進む必要があるかどうかを計算します。


回答ありがとうございます。将来のブラウザで明確にするために、2dクロス積の大きさの逆サインを取得すると、0〜90の値が得られます。2dクロス積のzコンポーネントのサインを取得すると、望ましい結果が得られます。
エラー454

@エラー454、あなたは絶対に正しい、私の投稿を修正しました。
テトラッド

1

ベクトルを使用して船をリダイレクトします。これが「ステアリング動作」の仕組みです。角度を計算する必要はなく、ベクトルを使用するだけです。これは計算上はるかに安価です。

ベクトルw(船舶1から船舶2へのベクトル)は、必要なすべての情報です。の加重バージョンを使用して、船1の速度ベクトルまたは船1の加速度ベクトル(または直接船首ベクトル)を変更しwます。

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

船1がコースからどれだけ離れているか(vがwとどれだけ一致しないか)の大きさは、(1 - dot(v,w))を使用して見つけることができます。

  • (正確にdot(v,w)整列するvと最大化wされます)
  • 1 - dot(v,w))はときに0を与えるvw完全に並んで設けられているvw)正規化されています

0

通常のジオメトリを介してベクトルの絶対角度を見つける簡単な方法があります。

例えば、ベクトルV = 2i-3j;

x係数の絶対値= 2;

y係数の絶対値= 3;

角度= atan(2/3); [角度は0から90の間です]

象限角に基づいて変更されます。

if(x係数<0およびy係数> 0)の場合、角度= 180角度。

(x係数<0およびy係数<0)の場合、角度= 180+角度。

(x係数> 0およびy係数<0)の場合、角度= 360角度;

if(x係数> 0およびy係数> 0)then angle = angle;

最初と2番目のベクトルの角度を見つけたら、2番目のベクトルから最初のベクトル角度を減算します。次に、2つのベクトル間の絶対角度を取得します。


5
これは、まさにatan2()関数が実装するものです。:)
ネイサンリード

@NathanReed、ええ、しかし、このメソッドをドット積で使用して、トリガーのオーバーヘッドを回避できませんでしたか?
jdk1.0

0

たぶん、少し違う質問を投稿して、自分で答えるべきでしょう。これは私が持っていたものに見つけることができる最も近い質問です。

私はHTMLキャンバスで2D作業を行っています。ここでは、ベクトルではなくラジアン単位の回転角度を使用しています。「現在の方位」(h)から「目標の方位」(t)に到達するには、「回転角」(ta)が必要でした。

私のソリューションは次のとおりです。

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.