การหมุนโดยพลการเกี่ยวกับทรงกลม


12

ฉันกำลังเขียนรหัสกลไกซึ่งช่วยให้ผู้ใช้สามารถเคลื่อนที่ไปรอบ ๆ พื้นผิวของทรงกลมได้ ตำแหน่งบนทรงกลมปัจจุบันถูกเก็บเป็นthetaและphiโดยที่thetaมุมระหว่างแกน z และการฉาย xz ของตำแหน่งปัจจุบัน (เช่นการหมุนรอบแกน y) และphiเป็นมุมจากแกน y ไปยังตำแหน่ง ฉันอธิบายว่าไม่ดี แต่มันเป็นหลักtheta = yaw,phi = pitch

Vector3 position = new Vector3(0,0,1);
position.X = (float)Math.Sin(phi) * (float)Math.Sin(theta);
position.Y = (float)Math.Sin(phi) * (float)Math.Cos(theta);
position.Z = (float)Math.Cos(phi);
position *= r;

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

ฉันเชื่อว่าฉันควรใช้ Quaternion เพื่อแสดงตำแหน่ง / ทิศทางบนทรงกลม แต่ฉันไม่สามารถคิดวิธีที่ถูกต้องในการทำมัน เรขาคณิตทรงกลมนั้นไม่เหมาะกับฉัน

โดยพื้นฐานแล้วฉันต้องกรอกข้อมูลบล็อกต่อไปนี้:

public void Move(Direction dir)
{   
    switch (dir)
    {
        case Direction.Left:
            // update quaternion to rotate left
            break;
        case Direction.Right:   
            // update quaternion to rotate right
            break;
        case Direction.Up:
            // update quaternion to rotate upward
            break;
        case Direction.Down:
            // update quaternion to rotate downward
            break;
    }
}

จะเกิดอะไรขึ้นถ้าผู้เล่นถึงเสา? ฉันสังเกตว่าคุณเขียนว่า "ทิศทางขึ้นไป" คุณหมายถึง "ขึ้นไป" อย่างแท้จริงหรือไม่ (นั่นคืออยู่ห่างจากพื้นผิวของทรงกลม), "ตรงไปข้างหน้า" หรือ "ไปทางขั้วโลกเหนือ" (สองหลังจะเหมือนกันถ้าผู้เล่น ไม่สามารถเปลี่ยนทิศทางของพวกเขาและ "ข้างหน้าพวกเขา" หรือ "ขึ้น" บนหน้าจอมักจะเป็นทิศเหนือ)?
Martin Sojka

บางทีมันก็พูดไม่ดี ผู้เล่นไม่ควรออกจากพื้นผิวของทรงกลมและไม่ควรระวังแกนของคาร์ดินัล ดังนั้นเมื่อคุณเลื่อน "ขึ้น" คุณจะเคลื่อนที่ไปตามพื้นผิวของทรงกลมในทิศทางขึ้นไปโดยสัมพันธ์กับทิศทางของผู้เล่น เช่นถ้าคุณอยู่ที่ (r, 0,0) และกดขึ้นคุณจะไปที่เสา z + แต่ถ้าคุณไปเรื่อย ๆ คุณควรพันไปเรื่อย ๆ
azz

ยังมีอีกหนึ่งคำถาม: ผู้เล่นสามารถเปลี่ยนการวางแนว (หมุน "ซ้าย" และ "ขวา") ได้หรือไม่
Martin Sojka

อาจจะเป็นตัวอย่างที่ดีของสิ่งที่ฉันจะให้: ผู้เล่นที่(1,1,1)ถือหุ้นที่เหลือจะหมุนรอบทรงกลมผ่าน(~1.2,0,~-1.2)แล้ว(-1,-1,-1)แล้วและกลับมาที่(~-1.2,0,~1.2) (1,1,1)
azz

1
หากคุณต้องการติดตามthetaและphiอัปเดตตำแหน่งของคุณอยู่เสมอคุณกำลังทำให้ปัญหาของคุณซับซ้อนโดยไม่จำเป็น ง่ายกว่ามากในการคำนวณ 2 แกนการหมุนแต่ละเฟรม (หนึ่งในนั้น (หาว) ไม่เคยเปลี่ยนแปลง) และVector3.Transormรอบ ๆ ทรงกลม นี้จะลดความซับซ้อนของปัญหาของคุณ แต่จะทำให้คุณตัดการเชื่อมต่อกับ&phi theta
Steve H

คำตอบ:


5

ที่จริงแล้วมันกลับกลายเป็นว่าคุณไม่สามารถมี 'ทั้งสองวิธี': หากความตั้งใจของคุณคือการไม่มีความรู้สึก 'การปฐมนิเทศสัมบูรณ์' บนทรงกลม (นั่นคือถ้าผู้เล่นไม่ได้หันหน้าเข้าหาเสาเสมอ ) จากนั้นคุณจะต้องมีแนวคิดเกี่ยวกับการวางแนวของผู้เล่น นี่เป็นเพราะตรงกันข้ามกับสิ่งที่สัญชาตญาณอาจแนะนำการเคลื่อนที่บนทรงกลมนั้นไม่เหมือนกับการเคลื่อนที่บนเครื่องบิน ความโค้งที่แท้จริงของทรงกลมหมายความว่าผู้เล่นสามารถดำเนินการที่จะหมุนตัวเอง!

สำหรับตัวอย่างที่ดีที่สุดของสิ่งที่ฉันกำลังพูดถึงให้จินตนาการว่าผู้เล่นเริ่มต้นที่จุดหนึ่งบนเส้นศูนย์สูตร (เพื่อความสะดวกสบายเราจะจินตนาการว่าหน้าปัดนาฬิกาเทียบเคียงกับเส้นศูนย์สูตรจากด้านบนและทำให้ผู้เล่นอยู่ที่ 6 โมง ) หันหน้าขึ้น - นั่นคือไปทางขั้วโลกเหนือ สมมติว่าผู้เล่นเดินไปตลอดทางจนถึงขั้วโลกเหนือ จากนั้นพวกเขาจะหันเข้าหาจุด 12 นาฬิกาโดยตรง ตอนนี้ให้ผู้เล่นย้ายไปทางขวาของพวกเขาจากขั้วโลกเหนือกลับไปที่เส้นศูนย์สูตร พวกเขาจะไขลานที่จุด 3 นาฬิกา - แต่เพราะใบหน้าของพวกเขาไม่เปลี่ยนเมื่อพวกเขาขยับไปทางขวา(ความคิดคือใบหน้าของพวกเขาไม่เปลี่ยนแปลงไม่ว่าพวกเขาจะเคลื่อนไหวอย่างไร) พวกเขาจะยังคงเผชิญหน้ากับจุด 12 นาฬิกาตอนนี้พวกเขากำลังเผชิญหน้ากับเส้นศูนย์สูตร! ตอนนี้ให้พวกเขาย้าย 'ถอยหลัง' กลับไปที่จุดเริ่มต้น (6 โมง) จากนั้นพวกเขาจะยังคงหันหน้าไปตามเส้นศูนย์สูตรดังนั้นพวกเขาจะหันไปทางจุด 3 นาฬิกา - เพียงแค่เคลื่อนที่ไปตามทรงกลมโดยไม่เปลี่ยนการวางแนว 'ส่วนตัว' ของพวกเขาทำให้พวกเขาหมุนจากหันหน้าไปทางขั้วโลกเหนือเพื่อ หันหน้าเข้าหาเส้นศูนย์สูตร! เรียกได้ว่านี่คือการบรรเลงอย่างละเอียดของ 'นักล่าที่เคลื่อนไหวไปทางทิศใต้หนึ่งไมล์ทางตะวันตกและจากนั้นก็เป็นเรื่องตลกที่อยู่ทางเหนือหนึ่งไมล์ - แต่ที่นี่เราใช้ประโยชน์จากความโค้งของทรงกลมเพื่อเปลี่ยนทิศทาง โปรดทราบว่าเอฟเฟกต์เดิมยังคงเกิดขึ้นแม้ในสเกลที่เล็กลง

โชคดีที่ quaternions ทำ (ตามที่คุณจดบันทึกไว้) จัดการกับสถานการณ์นี้ เนื่องจาก quaternion แสดงถึงการหมุนตามอำเภอใจมันแสดงถึงจุดและทิศทางของจุดบนแกนทรงกลมได้อย่างมีประสิทธิภาพ: จินตนาการเริ่มต้นด้วย 'triaxis' ที่จุดกำเนิดและให้การหมุนตามอำเภอใจแล้วย้ายหนึ่งหน่วยในทิศทางใดก็ตามของแกนหมุน จุดแกน Z ความคิดเล็กน้อยจะทำให้คุณเชื่อว่าสิ่งนี้จะนำคุณไปสู่จุดบนหน่วยทรงกลมด้วย 'การวางแนว' บางอย่าง (เช่นการจัดเรียงของแกน X และ Y ของ Triaxis ของคุณ) และคุณสามารถไปยังทุกจุด + การวางแนวบน หน่วยทรงกลมด้วยวิธีนี้ (เพียงกำหนดแกน Z ของคุณให้ชี้ตามเส้นจากจุดกำเนิดไปยังจุดที่อยู่บนทรงกลมจากนั้นส่งทริกซ์กลับไปที่จุดกำเนิดตามเส้นนั้น) มีอะไรอีก, เนื่องจากการคูณ quaternions สอดคล้องกับองค์ประกอบของการหมุนแต่ละการดำเนินการที่คุณอธิบายสามารถแสดงโดยการคูณ 'การวางแนวปัจจุบัน' ของคุณโดยการเลือก quaternion ที่เหมาะสม: โดยเฉพาะตั้งแต่ quaternion (หน่วย) (qx, qy, qz, qw) แปลว่า 'หมุนเกี่ยวกับแกน (qx, qy, qz) โดย arccos (qw)' จากนั้น (ขึ้นอยู่กับตัวเลือกเฉพาะของระบบพิกัดและให้ c_a เป็น cos (alpha) และ s_a sin (alpha) สองตัว สาม quaternions M_x = (s_a, 0, 0, c_a), M_y = (0, s_a, 0, c_a), และ M_z = (0, 0, s_a, c_a) จะแสดง 'หมุน (เช่นย้าย) ในทิศทางที่ฉัน 'm กำลังเผชิญโดย alpha' และ 'หมุนในทิศทาง orthogonal ไปยังที่ฉันกำลังเผชิญโดย alpha' (สามในสี่ส่วนเหล่านั้นจะเป็นตัวแทน 'หมุนตัวละครของฉันเกี่ยวกับแกนของเขาเอง'Cur_q = M_x * Cur_qถ้าผู้เล่นกดขึ้นหรือCur_q = M_y * Cur_qถ้าผู้เล่นกดขวา (หรืออาจเป็นไปได้ว่าCur_q = M_yinv * Cur_qถ้าผู้เล่นกดซ้ายซึ่ง M_yinv คือ 'ผกผัน' ของ M_y quaternion แสดงการหมุนในอีกทางหนึ่ง) โปรดทราบว่าคุณจะต้องระมัดระวังซึ่ง 'ด้าน' ที่คุณใช้การหมุนบนไม่ว่าจะก่อนหรือหลังการคูณ เพื่อความตรงไปตรงมาอาจเป็นวิธีที่ง่ายที่สุดในการแก้ปัญหาด้วยการลองผิดลองถูกทั้งสองแบบ

การเปลี่ยนจาก quaternion ที่ปรับปรุงใหม่ของคุณไปยังจุดบนทรงกลม (และการปรับตัวของคุณ) นั้นค่อนข้างตรงไปตรงมาเช่นกัน: โดยการโต้ตอบของย่อหน้าสุดท้ายสิ่งที่คุณต้องทำคือใช้ quaternion ของคุณบนพื้นฐานเวกเตอร์ (1, 0,0), (0,1,0) และ (0,0,1) ของเฟรมของคุณผ่านการดำเนินการ 'หมุนเวกเตอร์โดย quaternion' v → qvq -1 (โดยการคูณที่นี่คือการคูณ quaternion และเราระบุเวกเตอร์ v = (x, y, z) ด้วย 'degenerate quaternion' (x, y, z, 0) ตัวอย่างเช่นตำแหน่งบนทรงกลมของหน่วยได้รับโดยการแปลง z เวกเตอร์: pos = (qx, qy, qz, qw) * (0, 0, 1, 0) * (-qx, -qy, -qz, qw) = (qx, qy, qz, qw) * (qy, -qx, qw, qz) = (2 (qy * qw + qz * qx), 2 (qz * qy-qw * qx), (qz ^ 2 + qw ^ 2) - (qx ^ 2 + qy ^ 2), 0) ดังนั้น(2(qy*qw+qz*qx), 2(qz*qy-qw*qx), (qz^2+qw^2)-(qx^2+qy^2))จะเป็นพิกัดของผู้ใช้ที่ 'แปลง' ในหน่วยของทรงกลม (และเพื่อให้ได้พิกัดบนทรงกลมตามอำเภอใจแน่นอนคุณจะคูณพวกมันด้วยรัศมีของทรงกลม); การคำนวณที่คล้ายกันใช้งานได้กับแกนอื่น ๆ เพื่อกำหนดเช่นทิศทางหันหน้าของผู้ใช้


สิ่งที่ฉันต้องการสำเร็จ ฉันไม่สามารถคิดวิธีที่ถูกต้องเพื่อให้ได้ตำแหน่งจากการปฐมนิเทศ เมื่อใช้สิ่งที่คุณเตรียมไว้ฉันสามารถเขียนMove()ขั้นตอนได้ แต่เพื่อให้ได้แกนปกติ (เช่นตำแหน่งของฉัน) ฉันจะทำ(sin(qx),sin(qy),sin(qw)) * rอย่างไร
azz

@ ไม่แน่นอน - ฉันจะอัปเดตโพสต์ของฉันพร้อมรายละเอียด แต่รุ่นสั้น ๆ คือคุณใช้ quaternion ของคุณในการแปลงเวกเตอร์หน่วยเช่น (0,0,1) โดย v -> qvq <sup> -1 </sup> การทำงาน; ความจริงที่ว่าคุณกำลังแปลงเวกเตอร์ง่ายหมายความว่า (โดยธรรมชาติ) มีทางลัดที่นี่ แต่พิกัดสุดท้ายเป็นกำลังสองในค่าของควอเทอร์เนียนของคุณไม่ใช่เชิงเส้น
Steven Stadnicki

1

ฉันคิดว่าคุณต้องการสิ่งที่คล้ายกับhttp://www.youtube.com/watch?v=L2YRZbRSD1kนี้

ฉันพัฒนามันเป็นเวลา 48 ชั่วโมง gamejam ... คุณสามารถ donwload รหัสได้ที่นี่ ... http://archive.globalgamejam.org/2011/evil-god

ฉันใช้สิ่งที่คล้ายกับรหัสของคุณเพื่อรับ coords 3D ... แต่ฉันหมุนดาวเคราะห์และผู้เล่นอยู่ใน pos เดียวกันฉันคิดว่าคุณมีความสนใจในการเคลื่อนไหวสิ่งมีชีวิตคือ:

    // To add movement
    protected override void LocalUpdate(float seconds)
    {
        Creature.Alfa += Direction.X * seconds * Speed;
        Creature.Beta += Direction.Y * seconds * Speed;            
    }


    // To calculate position
       World.Planet.GetCartesian(Alfa, Beta, out Position); // as you do
       Matrix PositionMatrix = Matrix.CreateTranslation(Position) * World.Planet.RotationMatrix;           
       LastPositionAbsolute = PositionAbsolute;
       Vector3 Up = PositionAbsolute = Vector3.Transform(Vector3.Zero, PositionMatrix);           
       Up.Normalize();
       // This is to add and offset to the creature model position
       PositionAbsolute += Up * 8;  
      // calculate new forward vector if needed

       if ((PositionAbsolute - LastPositionAbsolute).Length() > 0.1f) {
           Forward = PositionAbsolute - LastPositionAbsolute;
           Forward.Normalize();
       }

       // Calculate the world transform with position, forward vector and up vector
       Matrix LocalWorld = Matrix.CreateWorld(PositionAbsolute, Forward, Up); 

       Transform = Matrix.CreateScale(Scale * ScaleFactor) * LocalWorld;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.