Ada lebih dari satu cara untuk melakukannya. Anda dapat menghitung orientasi absolut atau rotasi relatif terhadap avatar Anda, itu berarti orientasi baru Anda = avatarOrientation * q. Inilah yang terakhir:
Hitung sumbu rotasi dengan mengambil produk silang dari vektor unit forward avatar Anda dan vektor unit dari avatar ke target, vektor forward baru:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
Hitung sudut rotasi menggunakan produk-titik
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
Buat angka empat menggunakan rotAxis dan rotAngle dan gandakan dengan orientasi avatar saat ini
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
Jika Anda perlu bantuan menemukan vektor maju avatar saat ini, input untuk 1. tembak saja :)
EDIT: Menghitung orientasi absolut sebenarnya sedikit lebih mudah, gunakan vektor forward dari matriks identitas daripada vektor forward avatar sebagai input untuk 1) dan 2). Dan jangan gandakan dalam 3), alih-alih gunakan langsung sebagai orientasi baru:newRot = q
Penting untuk dicatat: Solusi ini memiliki 2 anomali yang disebabkan oleh sifat dari produk-silang:
Jika vektor maju sama. Solusi di sini adalah hanya mengembalikan angka empat identitas
Jika titik vektor tepat di arah yang berlawanan. Solusi di sini adalah membuat angka empat dengan menggunakan sumbu avatar sebagai sumbu rotasi dan sudut 180,0 derajat.
Berikut ini adalah implementasi dalam C ++ yang menangani kasus tepi tersebut. Mengubahnya ke C # seharusnya mudah.
// 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: Versi diperbaiki dari kode XNA Marc
// 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);
}
0*(rotation A) + 1*(rotation B)
- dengan kata lain, Anda hanya mengatur rotasi ke rotasi B. Slerp hanya untuk menentukan seperti apa rotasi itu (0% <x <100%) dari jalan di antaranya.