Bagaimana saya bisa menghitung sudut dan arah belokan yang tepat antara dua vektor 2D?

26

Saya sedang mengerjakan beberapa gerakan AI di mana tidak ada rintangan dan gerakan terbatas pada pesawat XY. Saya menghitung dua vektor, v , arah menghadap kapal 1, dan w , vektor menunjuk dari posisi kapal 1 ke kapal 2.

Saya kemudian menghitung sudut antara dua vektor ini menggunakan rumus

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

Masalah yang saya alami adalah bahwa arccoshanya mengembalikan nilai antara 0 ° dan 180 °. Ini membuat tidak mungkin untuk menentukan apakah saya harus belok kiri atau kanan untuk menghadapi kapal lain.

Apakah ada cara yang lebih baik untuk melakukan ini?

Kesalahan 454
sumber
1
Jika Anda menggunakan Unity, periksa Mathf.DeltaAngle().
Russell Borogove

Jawaban:

22

Jauh lebih cepat menggunakan produk lintas 2d. Tidak ada fungsi trigonometri yang mahal.

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

Jika Anda masih membutuhkan sudut yang sebenarnya saya sarankan menggunakan atan2. atan2akan memberi Anda sudut absolut dari vektor apa pun. Untuk mendapatkan sudut relatif antara vektor apa pun, hitung sudut absolutnya dan gunakan pengurangan sederhana.

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;
deft_code
sumber
1
Setelah membaca jawaban @ Tetrad saya kira Anda bisa menggabungkan produk silang dan arccos. Dengan cara ini Anda hanya akan menggunakan satu fungsi trigonometri, tetapi masih memiliki sudut yang sebenarnya. Namun saya sarankan menentang optimasi ini sampai Anda yakin pelacakan sudut AI Anda membuat dampak nyata pada kinerja game Anda.
deft_code
2
Ya, saat mengonversi antara vektor dan sudut, atan2 () pasti adalah teman Anda.
bluescrn
1
Terima kasih! Saya telah menemukan bahwa saya sebenarnya tidak benar-benar membutuhkan sudut, meraih produk lintas 2D cukup sederhana untuk kebutuhan saya.
Kesalahan 454
2
Cek if( cross == -0.0f )vs Anda juga if( cross == 0.0f )terlihat sangat rapuh.
bobobobo
1
@obobobo, dengan mesin fisika mungkin bukan pilihan untuk hanya memilih arah dan bergerak. Mengubah secara ajaib dapat menyebabkan mesin fisik menjadi panik. Rotasi lebih lanjut secara ajaib terlihat mengerikan untuk animasi juga. Jadi ya, Anda dapat menyelesaikan ini tanpa gagasan kiri atau kanan, tetapi solusi yang dipoles sering membutuhkan ini.
deft_code
10

Gunakan arcsin produk lintas 2D (yaitu komponen z dari vektor produk silang). Itu akan memberi Anda -90 hingga 90 yang akan memberi tahu Anda apakah harus ke kiri atau kanan.

Hati-hati karena A cross B tidak sama dengan B cross A.

Strategi lain (tapi mungkin tidak lurus ke depan) adalah menghitung "heading" dari dua vektor menggunakan atan2 dan kemudian mencari tahu apakah A menunjuk pada derajat X perlu ke kiri atau kanan untuk pergi ke B menunjuk pada derajat y.

Tetrad
sumber
Terima kasih atas tanggapannya. Agar lebih jelas untuk browser masa depan, mengambil sinus terbalik dari besarnya produk silang 2d akan menghasilkan nilai antara 0 dan 90. Mengambil sinus komponen-z dari produk silang 2d menghasilkan hasil yang diinginkan.
Kesalahan 454
@Error 454, Anda memang benar, memperbaiki posting saya.
Tetrad
1

Gunakan vektor untuk mengarahkan ulang kapal. Ini adalah cara kerja "kemudi perilaku" - Anda tidak perlu menghitung sudut, cukup gunakan vektor yang Anda miliki. Komputasi ini jauh lebih murah.

Vektor w(vektor dari Kapal 1 ke Kapal 2) adalah semua informasi yang Anda butuhkan. Ubah salah satu vektor kecepatan 1 kapal atau vektor percepatan 1 kapal (atau bahkan vektor heading langsung) menggunakan versi tertimbang w.

masukkan deskripsi gambar di sini

The besarnya seberapa jauh dari kapal 1 adalah tentu saja (seberapa parah v tidak cocok dengan w) dapat ditemukan dengan menggunakan ( 1 - dot(v,w))

  • ( dot(v,w)Dimaksimalkan kapan vdan wberbaris tepat)
  • ( 1 - dot(v,w)) memberi 0 kapan vdan wbenar-benar berbaris, disediakan vdan wdinormalisasi)
bobobobo
sumber
0

Ada cara sederhana untuk menemukan sudut absolut vektor melalui geometri normal.

misalnya vektor V = 2i - 3j;

nilai absolut dari x koefisien = 2;

nilai absolut dari koefisien y = 3;

angle = atan (2/3); [sudut akan berada di antara 0 hingga 90]

Berdasarkan sudut kuadran akan diubah.

jika (koefisien x <0 dan koefisien y> 0) maka sudut = 180-sudut;

jika (x koefisien <0 dan koefisien y <0) maka sudut = 180 + sudut;

jika (x koefisien> 0 dan koefisien y <0) maka sudut = 360-sudut;

jika (x koefisien> 0 dan koefisien y> 0) maka sudut = sudut;

setelah menemukan sudut vektor pertama dan kedua, cukup kurangi sudut vektor pertama dari vektor kedua. Maka Anda akan mendapatkan sudut absolut antara dua vektor.

manojyerra
sumber
5
Inilah yang mengimplementasikan fungsi atan2 () untuk Anda. :)
Nathan Reed
@NathanReed, yeah, tetapi tidak bisakah Anda menggunakan metode ini dengan produk titik untuk menghindari biaya overhead trigonometri?
jdk1.0
0

Mungkin saya harus memposting pertanyaan yang sedikit berbeda dan menjawabnya sendiri; ini adalah pertanyaan terdekat yang bisa saya temukan dengan pertanyaan yang saya miliki.

Saya melakukan pekerjaan 2D di kanvas html, di mana saya memiliki sudut rotasi di radian daripada vektor. Saya membutuhkan "turn angle" (ta) untuk mendapatkan dari "heading saat ini" (h) ke "target heading" (t).

Inilah solusi saya:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
Shavais
sumber