İki 2D vektör arasındaki açıyı ve doğru dönüş yönünü nasıl hesaplayabilirim?


26

Engel bulunmayan bazı hareketler üzerinde çalışıyorum ve hareket XY düzlemiyle sınırlı. İki vektörü ( v) , geminin (1) karşı yönünü ve w'nin , geminin (1) konumundan gemiyi (2) işaret ettiğini gösteren vektörü hesaplıyorum .

Daha sonra bu iki vektör arasındaki açıyı aşağıdaki formülü kullanarak hesaplıyorum.

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

Karşılaştığım sorun arccosyalnızca 0 ° ile 180 ° arasındaki değerleri döndürmesi. Bu, diğer gemiyle yüzleşmek için sola mı yoksa sağa mı dönmem gerektiğini belirlememi imkansız kılıyor.

Bunu yapmanın daha iyi bir yolu var mı?


1
Unity kullanıyorsanız, kontrol edin Mathf.DeltaAngle().
Russell Borogove

Yanıtlar:


22

Bir 2d çapraz ürün kullanmak çok daha hızlı. Hiçbir pahalı trig işlevi yoktur.

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

Hala gerçek açılara ihtiyacınız varsa kullanmanızı öneririm atan2. atan2size herhangi bir vektörün mutlak açısını verecektir. Herhangi bir vektör arasındaki göreceli açıyı elde etmek için, mutlak açılarını hesaplayın ve basit çıkarma kullanın.

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'ın cevabını okuduktan sonra, bir çarpı çarpım ile bir arccos. Bu şekilde sadece bir trig fonksiyonu kullanacaksınız, fakat hala gerçek açıya sahip olacaksınız. Ancak, AI açısı izlemenin oyununuzun performansı üzerinde gözle görülür bir etki yarattığından emin olana kadar bu optimizasyona karşı öneriyorum.
deft_code 16

2
Evet, vektörler ve açılar arasında dönüştürme yaparken atan2 () kesinlikle arkadaşınızdır.
bluescrn

1
Teşekkürler! Aslında bu açıya gerçekten ihtiyacım olmadığını, 2B çapraz ürünü kapmak ihtiyaçlarım için yeterince basit olduğunu öğrendim.
Hata 454

2
Ayrıca senin if( cross == -0.0f )vs if( cross == 0.0f )çek son derece kırılgan görünüyor.
bobobobo

1
@bobobobo, bir fizik motoruyla, sadece bir yön seçip hareket etmek bir seçenek olmayabilir. Sihirli dönüş, fizik motorlarının çılgına dönmesine neden olabilir. Daha da büyülü bir şekilde dönen animasyon için de korkunç görünüyor. Yani evet, olabilir , sağdan sola veya bir fikri olmadan bu çözmek ama cilalı çözüm genellikle bu ihtiyacı var.
deft_code

10

2B çapraz ürünün arkini kullanın (yani çapraz ürün vektörünün z bileşeni). Bu size -90 ile 90 arasında bir değer verecek.

Dikkatli olun, çünkü A çarpı B, B çarpı A ile aynı değildir.

Diğer bir strateji (ancak muhtemelen ileri doğru değil), atan2 kullanarak iki vektörün "yönünü" hesaplamak ve sonra X derecelerinde A işaretinin Y derecesinde B işaretine gitmek için sola mı yoksa sağa mı gitmesi gerektiğini bulmaktır.


Cevap için teşekkürler. Gelecekteki tarayıcılar için açık olması gerekirse, 2d çapraz ürünün büyüklüğünün ters sinüsünü almak, 0 ile 90 arasında değerler verir. 2d çapraz ürünün z bileşeninin sinüsünü almak, istenen sonuçları verir.
Hata 454

@Error 454, kesinlikle haklısın, yazımı düzeltti.
Tetrad

1

Gemiyi yönlendirmek için vektörleri kullanın . "Direksiyon davranışları" böyle çalışır - açıyı hesaplamanıza gerek kalmaz, sadece sahip olduğunuz vektörleri kullanın. Bu hesaplama açısından çok daha ucuz.

Vektör w(Gemi 1'den Gemi 2'ye kadar vektör) ihtiyacınız olan tüm bilgilerdir. Ağırlıklı bir versiyonunu kullanarak, ya gemi 1'in hız vektörünü ya da gemi 1'in hızlanma vektörünü (hatta doğrudan rota vektörünü) değiştirin w.

görüntü tanımını buraya girin

Büyüklüğü ne kadar geminin 1 kapalı tabii kapalıdır kullanılarak bulunabilir (v w ile eşleşmeyen ne kadar kötü) ( 1 - dot(v,w))

  • ( tam olarak dot(v,w)ne zaman vve wsıraya çıkar)
  • ( 1 - dot(v,w)) verilen ve normalize olduğu zaman vve wtamamen sıralandığında 0 verir )vw

0

Bir vektörün mutlak açısını normal geometri ile bulmanın basit bir yolu vardır.

örneğin vektör V = 2i - 3j;

x katsayısının mutlak değeri = 2;

y katsayısının mutlak değeri = 3;

açı = atanmış (2/3); [açı 0 ila 90 arasında olacak]

Kadran açısına göre değişecektir.

eğer (x katsayısı <0 ve y katsayısı> 0) o zaman açı = 180 açı;

eğer (x katsayısı <0 ve y katsayısı <0) o zaman açı = 180 + açı;

eğer (x katsayısı> 0 ve y katsayısı <0) o zaman açı = 360-açı;

eğer (x katsayısı> 0 ve y katsayısı> 0) o zaman açı = açı;

birinci ve ikinci vektörlerin açısını bulduktan sonra, ikinci vektörden ilk vektör açısını çıkarmanız yeterlidir. Sonra iki vektör arasında mutlak açı elde edersiniz.


5
Bu tam olarak atan2 () işlevinin sizin için uyguladığı şeydir. :)
Nathan Reed

@NathanReed, evet, ancak bu yöntemi, tepegöz yükünden kaçınmak için bir nokta ürünle kullanamaz mıydınız?
jdk1.0

0

Belki biraz farklı bir soru gönderip kendime cevap vermeliyim; bu sahip olduğum soruyu bulabileceğim en yakın soru.

Html tuvalinde 2B çalışma yapıyorum, burada vektörlerden ziyade radyan olarak dönüş açılarım var. "Geçerli başlık" (h) "hedef başlık" (t) almak için "dönüş açısı" (ta) gerekli.

İşte benim çözümüm:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.