จัดวางโมเดลเพื่อเผชิญหน้ากับเป้าหมาย


28

ฉันมีสองวัตถุ (เป้าหมายและผู้เล่น) ทั้งสองมีตำแหน่ง (Vector3) และการหมุน (Quaternion) ฉันต้องการให้เป้าหมายหมุนและหันไปทางขวาที่โปรแกรมเล่น เป้าหมายเมื่อมีบางสิ่งบางอย่างควรยิงไปที่ผู้เล่น

ฉันได้เห็นตัวอย่างของการเล่น slerping กับผู้เล่นมากมาย แต่ฉันไม่ต้องการการหมุนแบบเพิ่มขึ้นดีฉันคิดว่ามันจะโอเคตราบใดที่ฉันสามารถทำให้ slerp เป็น 100% และตราบใดที่มันใช้งานได้จริง

FYI - ฉันสามารถใช้ตำแหน่งและการหมุนเพื่อทำสิ่งอื่นมากมายและมันใช้งานได้ดีมากยกเว้นชิ้นสุดท้ายที่ฉันไม่สามารถหาได้

ตัวอย่างโค้ดทำงานในคลาสของเป้าหมาย, ตำแหน่ง = ตำแหน่งเป้าหมาย, Avatar = ผู้เล่น

แก้ไข

ตอนนี้ฉันใช้รหัส c # ของ Maik ที่เขาให้มาและใช้งานได้ดี!


4
หากคุณทำ slerp 100% แสดงว่าคุณไม่ได้ใช้ slerp คุณเพียงแค่ตั้งค่าการหมุนเป็น0*(rotation A) + 1*(rotation B)- ในคำอื่น ๆ คุณกำลังตั้งค่าการหมุนเป็นการหมุน B ทางยาว Slerp มีไว้สำหรับพิจารณาว่าการหมุนควรมีลักษณะอย่างไร (0% <x <100%) ของวิธีการในระหว่างนั้น
doppelgreener

ตกลงเข้าใจได้ แต่เป้าหมายยังไม่หมุนไปทางผู้เล่นอย่างเต็มที่ ... "ทางยาว" ด้วยรหัสนั้น
Marc

คำตอบ:


20

มีมากกว่าหนึ่งวิธีที่จะทำ คุณสามารถคำนวณการวางแนวสัมบูรณ์หรือการหมุนสัมพัทธ์กับอวาตาร์ของคุณซึ่งหมายถึงการวางแนวใหม่ของคุณ = avatarOrientation * q นี่คือหนึ่งหลัง:

  1. คำนวณแกนการหมุนโดยการใช้ครอสโปรดัคของเวกเตอร์ไปข้างหน้าของอวาตาร์ของคุณและเวกเตอร์ของหน่วยจากอวาตาร์ไปยังเป้าหมาย

    vector newForwardUnit = vector::normalize(target - avatarPosition);
    vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
  2. คำนวณมุมการหมุนโดยใช้ดอทโปรดัค

    float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
  3. สร้าง quaternion โดยใช้ rotAxis และ rotAngle แล้วคูณด้วยการวางแนวปัจจุบันของอวตาร

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

หากคุณต้องการความช่วยเหลือในการค้นหาเวกเตอร์ไปข้างหน้าปัจจุบันของอวตารอินพุตสำหรับ 1 เพียงแค่ยิง :)

แก้ไข:การคำนวณการวางแนวแบบสัมบูรณ์นั้นง่ายกว่าจริง ๆ ให้ใช้เวกเตอร์ไปข้างหน้าของ identity-matrix แทนที่จะเป็นเวกเตอร์ไปข้างหน้ารูปแทนตัวสำหรับ 1) และ 2) และอย่าคูณใน 3) แทนที่จะใช้โดยตรงเป็นทิศทางใหม่:newRot = q


สิ่งสำคัญที่ควรทราบ:วิธีการแก้ปัญหามี 2 ความผิดปกติที่เกิดจากธรรมชาติของผลิตภัณฑ์ข้าม:

  1. ถ้าเวกเตอร์ไปข้างหน้าเท่ากัน วิธีแก้ปัญหาที่นี่ก็แค่ส่งคืนควอเทอร์เนชันเฉพาะตัว

  2. หากเวกเตอร์ชี้ไปในทิศทางตรงกันข้าม วิธีแก้ปัญหาที่นี่คือการสร้างควอเทอเนียนโดยใช้อวตารขึ้นแกนเป็นแกนหมุนและมุม 180.0 องศา

นี่คือการดำเนินการใน C ++ ที่ดูแลกรณีขอบเหล่านั้น การแปลงเป็น C # ควรเป็นเรื่องง่าย

// 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);
}

แก้ไข:แก้ไขรหัส 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);
}

ตกลงฉันให้ภาพที่คุณเห็นโดยการแก้ไขของฉันในคำถามรหัสที่ให้ไว้ ไม่แน่ใจจริงๆว่าปัญหาคืออะไร a และ b คืออินพุตไปข้างหน้าเวกเตอร์หรืออย่างน้อยก็ควรจะเป็น
Marc

@ Marc เห็นรหัส XNA ที่ถูกต้องของฉันในคำตอบของฉัน มี 2 ​​ปัญหา: 1) การคำนวณของเวกเตอร์ไปข้างหน้าใหม่ผิดต้องเป็นปกติ AvatarPosition - TargetPosition 2) rotAxis จะต้องเป็นมาตรฐานหลังจาก cross-product ใน GetRotation
Maik Semder

@ Marc, การเปลี่ยนแปลงเล็กน้อย 2 รายการ: 3) ต้นทางและปลายทางได้รับการทำให้เป็นมาตรฐานอยู่แล้วไม่จำเป็นต้องทำให้เป็นปกติอีกครั้งใน GetRotation 4) การทดสอบ dont สำหรับสัมบูรณ์ 1 / -1 ใน GetRotation ค่อนข้างใช้ความอดทนฉันใช้ 0.000001f
Maik Semder

อืมก็ยังใช้งานไม่ได้ สิ่งที่ปรับขนาดเดียวกันนั้นเกิดขึ้นกับโมเดลและเป้าหมายไม่หมุนไปทางอวตาร (ซึ่งฉันสังเกตเห็นในความคิดเห็นของคุณว่าคุณกำลังพยายามที่จะหมุนอวตารไปสู่เป้าหมาย เพื่อรับม็อบหน้าผู้เล่น (อวตารในกล้องบุคคลที่ 3) เมธอด GetRotation ไม่ควรรู้อะไรเกี่ยวกับการหมุนเวียนของเป้าหมายและอวาตาร์ในขณะนี้คุณคิดว่า newForward นั้นถูกสร้างขึ้นอย่างถูกต้องหรือไม่?
Marc

ถ้าวัตถุถูกปรับอัตราส่วนนั่นหมายความว่า quaternion ไม่มีความยาวหน่วยนั่นหมายความว่า rotAxis ไม่ได้ถูกทำให้เป็นมาตรฐาน คุณได้เพิ่มการเปลี่ยนแปลงรหัสสุดท้ายของฉันด้วยการฟื้นฟู rotAxis? อย่างไรก็ตามโปรดแสดงรหัสปัจจุบันของคุณและสำหรับกรณีตัวอย่างที่มันไม่ทำงานโปรดนอกจากนี้ยังโพสต์ค่าของ: และnewForward rotAngle rotAxis returned quaternionรหัสสำหรับม็อบจะเหมือนกันเมื่อเราทำให้มันทำงานกับอวตารมันจะง่ายต่อการเปลี่ยนหัวเรื่องของวัตถุใด ๆ
Maik Semder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.