Bir modeli hedefle yüzleşmek için yönlendirme


28

İki nesnem var (hedef ve oyuncu), her ikisi de Konum (Vektör3) ve Döndürme (Quaternion). Hedefin dönmesini ve oyuncuya doğru bakmasını istiyorum. Hedef, bir şeyi vurduğunda, doğrudan oyuncuya ateş etmelidir.

Oynatıcıya çok sayıda slering örneği gördüm, ancak artımlı rotasyon istemiyorum, sanırım, slerp'ı% 100 yapabileceğim sürece ve gerçekten işe yaradığı sürece tamam olur.

FYI - Pozisyonu ve rotasyonu başka birçok şey yapmak için kullanabiliyorum ve çözemediğim bu son parça dışında hepsi harika çalışıyor.

Kod örnekleri Hedefin sınıfında çalışır, Konum = hedef konum, Avatar = oyuncu.

DÜZENLE

Şimdi Maik'in sağladığı c # kodunu kullanıyorum ve harika çalışıyor!


4
% 100 slerp yapıyorsanız, slerp kullanmıyorsunuzdur. Sadece dönüşü ayarlıyorsunuz 0*(rotation A) + 1*(rotation B)- başka bir deyişle, dönüşü yalnızca B dönüşü uzunluğuna ayarlıyorsunuz. Slerp, yalnızca rotasyonun aradaki gibi görünmesi gerektiğini (% 0 <% <% 100) belirlemek içindir.
doppelgreener

Tamam, mantıklı ama hedef hala oyuncuya doğru dönmüyor ... bu kodla "uzun yol".
Marc,

Yanıtlar:


20

Bunu yapmanın birden fazla yolu var. Mutlak yönelimi veya avatarınıza göre dönüşü hesaplayabilirsiniz; bu, yeni yönelim = avatarOrientation * q anlamına gelir. İşte ikincisi:

  1. Avatarınızın ünite ileri vektörünün çarpım çarpımını ve avatardan hedef vektör ünitenin yeni ileri vektörünü alarak dönüş eksenini hesaplayın:

    vector newForwardUnit = vector::normalize(target - avatarPosition);
    vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
  2. Nokta ürünü kullanarak dönüş açısını hesaplayın

    float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
  3. RotAxis ve rotAngle kullanarak kuaterniyon yaratın ve onu avatarın mevcut oryantasyonu ile çarpın

    quaternion q(rotAxis, rotAngle);
    quaternion newRot = avatarRot * q;

Avatarın o andaki ileri vektörünü bulmak için yardıma ihtiyacınız olursa, 1. girdi sadece :)

EDIT: Mutlak oryantasyonu hesaplamak aslında biraz daha kolaydır, 1) ve 2) 'nin girişi olarak avatarlar ileri vektörü yerine identity-matrisin ileri vektörünü kullanın. Ve 3 ile çarpmayın, bunun yerine doğrudan yeni yönelim olarak kullanın:newRot = q


Unutulmaması gereken önemli: Çözüm, çapraz ürünün yapısından kaynaklanan 2 anomaliye sahiptir:

  1. İleri vektörler eşitse. Buradaki çözüm basitçe kimlik kuaterniyonunu döndürmektir

  2. Vektörler tam tersi yönü gösteriyorsa. Buradaki çözüm kuaterniyonu, döndürme ekseni olarak avatarları yukarı ekseni ve 180.0 derece açısını kullanarak oluşturmaktır.

İşte C ++ 'da bu son durumlarla ilgilenen uygulama. C # 'ya dönüştürmek kolay olmalı.

// returns a quaternion that rotates vector a to vector b
quaternion get_rotation(const vector &a, const vector &b, const vector &up)
{   
    ASSERT_VECTOR_NORMALIZED(a);
    ASSERT_VECTOR_NORMALIZED(b);

    float dot = vector::dot(a, b);    
    // test for dot -1
    if(nearly_equal_eps_f(dot, -1.0f, 0.000001f))
    {
        // vector a and b point exactly in the opposite direction, 
        // so it is a 180 degrees turn around the up-axis
        return quaternion(up, gdeg2rad(180.0f));
    }
    // test for dot 1
    else if(nearly_equal_eps_f(dot, 1.0f, 0.000001f))
    {
        // vector a and b point exactly in the same direction
        // so we return the identity quaternion
        return quaternion(0.0f, 0.0f, 0.0f, 1.0f);
    }

    float rotAngle = acos(dot);
    vector rotAxis = vector::cross(a, b);
    rotAxis = vector::normalize(rotAxis);
    return quaternion(rotAxis, rotAngle);
}

EDIT: Marc'ın XNA kodunun düzeltilmiş versiyonu

// the new forward vector, so the avatar faces the target
Vector3 newForward = Vector3.Normalize(Position - GameState.Avatar.Position);
// calc the rotation so the avatar faces the target
Rotation = Helpers.GetRotation(Vector3.Forward, newForward, Vector3.Up);
Cannon.Shoot(Position, Rotation, this);


public static Quaternion GetRotation(Vector3 source, Vector3 dest, Vector3 up)
{
    float dot = Vector3.Dot(source, dest);

    if (Math.Abs(dot - (-1.0f)) < 0.000001f)
    {
        // vector a and b point exactly in the opposite direction, 
        // so it is a 180 degrees turn around the up-axis
        return new Quaternion(up, MathHelper.ToRadians(180.0f));
    }
    if (Math.Abs(dot - (1.0f)) < 0.000001f)
    {
        // vector a and b point exactly in the same direction
        // so we return the identity quaternion
        return Quaternion.Identity;
    }

    float rotAngle = (float)Math.Acos(dot);
    Vector3 rotAxis = Vector3.Cross(source, dest);
    rotAxis = Vector3.Normalize(rotAxis);
    return Quaternion.CreateFromAxisAngle(rotAxis, rotAngle);
}

Tamam, söz konusu düzenlemede kodun verdiği şekilde görebileceğiniz gibi bir şans verdim. Sorunun ne olduğundan emin değilim. a ve b girişleri ileri vektörlerdir veya en azından öyle olması gerektiğini varsayalım.
Marc

@Marc cevabımdaki XNA kodunun düzeltilmiş versiyonunu gör. 2 problem vardı: 1) yeni ileri vektörün hesaplanması yanlıştı, normalleştirilmelidir AvatarPosition - TargetPosition 2) rotAxis, GetRotation
Maik Semder'de

@Marc, 2 ufak değişiklik: 3) kaynak ve dest zaten normalleştirildi, GetRotation'da tekrar normalize etmelerine gerek yok 4) GetRotation'da mutlak 1 / -1 için test yapma, biraz tolerans kullan, 0.000001f kullandım
Maik Semder

Hmm, bu hala çalışmıyor. Aynı ölçekleme modeli ile de olur ve hedef avatara doğru dönmez (yorumlarınızda gördüğünüz gibi avatarı hedefe doğru döndürmeye çalışıyorsunuz ... tam tersi olmalı) ... temelde, denemek oyuncuya dönük bir mafya almak için (3. kişi kamerada avatar). GetRotation yöntemi, hedefin ve avatarın o anki rotasyonları hakkında bir şey bilmemeli mi, newForward'ın düzgün bir şekilde oluşturulduğunu düşünüyor musunuz?
Marc

Nesne ölçeklenirse, bu kuaterniyonun bir birim uzunluğa sahip olmadığı, yani rotAxis'in normalize olmadığı anlamına gelir. RotAxis'in normalleştirilmesiyle son kod değişikliğimi eklediniz mi? Ancak, lütfen geçerli kodunuzu ve çalışmadığı bir örnek durumda, lütfen ve: değerlerini de newForward rotAngle rotAxisyazın returned quaternion. Mafya kodu aynı olacak, avatarla çalışmasını sağladığımızda, herhangi bir nesnenin başlığını değiştirmek kolay olacak.
Maik Semder
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.