Định hướng một mô hình để đối mặt với một mục tiêu


28

Tôi có hai đối tượng (mục tiêu và người chơi), cả hai đều có Vị trí (Vector3) và Xoay (Đệ tứ). Tôi muốn mục tiêu xoay và hướng thẳng vào người chơi. Mục tiêu, khi nó bắn một cái gì đó nên bắn ngay vào người chơi.

Tôi đã thấy rất nhiều ví dụ về việc làm chậm trình phát cho người chơi, nhưng tôi không muốn xoay vòng tăng dần, tôi cho rằng điều đó sẽ ổn miễn là tôi có thể làm cho slerp được 100%, và miễn là nó thực sự hoạt động.

FYI - Tôi có thể sử dụng vị trí và xoay để làm nhiều việc khác và tất cả đều hoạt động tốt, ngoại trừ phần cuối cùng này tôi không thể tìm ra.

Các mẫu mã chạy trong lớp của Target, Position = vị trí mục tiêu, Avatar = người chơi.

CHỈNH SỬA

Bây giờ tôi đang sử dụng mã c # của Maik mà anh ấy đã cung cấp và nó hoạt động rất tốt!


4
Nếu bạn đang thực hiện slerp 100%, bạn không sử dụng slerp. Bạn chỉ cài đặt xoay thành 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.
doppelgreener

Ok, có ý nghĩa, nhưng, mục tiêu vẫn không hoàn toàn xoay về phía người chơi ... "chặng đường dài" với mã đó.
Marc

Câu trả lời:


20

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:

  1. 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);
  2. 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));
  3. 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:

  1. 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

  2. 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);
}

Ok, tôi đã đưa ra một cú đánh như bạn có thể thấy bằng cách chỉnh sửa của tôi trong câu hỏi, mã được cung cấp. Không thực sự chắc chắn vấn đề là gì. đầu vào a và b là các vectơ chuyển tiếp, hoặc ít nhất là giả sử là.
Marc

@Marc xem phiên bản sửa lỗi của mã XNA của bạn trong câu trả lời của tôi. Có 2 vấn đề: 1) tính toán của vectơ chuyển tiếp mới là sai, phải được chuẩn hóa AvatarPocation - TargetPocation 2) rotAxis phải được chuẩn hóa sau sản phẩm chéo trong GetRotation
Maik Semder 24/07/11

@Marc, 2 thay đổi nhỏ: 3) nguồn và số phận đã được chuẩn hóa, không cần phải chuẩn hóa lại chúng trong GetRotation 4) không kiểm tra tuyệt đối 1 / -1 trong GetRotation, thay vào đó hãy sử dụng một số dung sai, tôi đã sử dụng
0,000001f

Hmm, đó vẫn không hoạt động. Điều tương tự xảy ra với mô hình và mục tiêu không xoay theo hình đại diện (mà tôi nhận thấy trong các nhận xét của bạn, bạn đang cố gắng xoay hình đại diện về phía mục tiêu .... nên theo cách khác) ... về cơ bản, đang cố gắng để có được một mob để đối mặt với người chơi (hình đại diện trong camera của người thứ 3). Phương thức GetRotation không nên biết điều gì đó về các góc quay hiện tại của mục tiêu và hình đại diện, như trong, bạn có nghĩ rằng newForward đang được tạo đúng không?
Marc

Nếu đối tượng được chia tỷ lệ, điều đó có nghĩa là bậc bốn không có độ dài đơn vị, điều đó có nghĩa là rotAxis không được chuẩn hóa. Bạn đã thêm thay đổi mã cuối cùng của tôi với việc chuẩn hóa rotAxis chưa? Tuy nhiên, vui lòng hiển thị mã hiện tại của bạn và trong trường hợp ví dụ không hoạt động, vui lòng đăng các giá trị của: newForward rotAngle rotAxisreturned quaternion. Mã cho mob sẽ giống nhau, một khi chúng tôi làm cho nó hoạt động với hình đại diện, sẽ dễ dàng thay đổi tiêu đề của bất kỳ đối tượng nào.
Maik
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.