Có nhiều hơn một cách để làm điều đó. Bạn có thể tính toán hướng tuyệt đối hoặc xoay tương ứng với hình đại diện của mình, điều đó có nghĩa là hướng mới của bạn = avatarOrientation * q. Đây là cái sau:
Tính toán trục xoay bằng cách lấy sản phẩm chéo của vectơ chuyển tiếp đơn vị của hình đại diện của bạn và vectơ đơn vị từ hình đại diện đến mục tiêu, vectơ chuyển tiếp mới:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
Tính góc quay bằng cách sử dụng sản phẩm chấm
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
Tạo tứ phân vị bằng cách sử dụng rotAxis và rotAngle và nhân nó với định hướng hiện tại của avatar
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
Nếu bạn cần trợ giúp tìm vector chuyển tiếp hiện tại của avatar, đầu vào cho 1. chỉ cần bắn :)
EDIT: Tính toán định hướng tuyệt đối thực sự dễ dàng hơn một chút, sử dụng vectơ chuyển tiếp của ma trận danh tính thay vì vectơ chuyển tiếp hình đại diện làm đầu vào cho 1) và 2). Và đừng nhân nó lên 3), thay vào đó hãy sử dụng nó trực tiếp như định hướng mới:newRot = q
Điều quan trọng cần lưu ý: Giải pháp có 2 sự bất thường gây ra bởi bản chất của sản phẩm chéo:
Nếu các vectơ chuyển tiếp bằng nhau. Giải pháp ở đây chỉ đơn giản là trả lại danh tính
Nếu các vectơ chỉ chính xác theo hướng ngược lại. Giải pháp ở đây là tạo ra bậc bốn bằng cách sử dụng hình đại diện lên trục làm trục quay và góc 180,0 độ.
Đây là cách thực hiện trong C ++, xử lý các trường hợp cạnh đó. Chuyển đổi nó thành C # nên dễ dàng.
// 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: Phiên bản chính xác của mã XNA của 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)
- nói cách khác, bạn chỉ cài đặt xoay để xoay B theo cách xa. Slerp chỉ để xác định vòng xoay sẽ trông như thế nào (0% <x <100%) ở giữa.