Tidak, ini bukan bug mesin atau artefak dari representasi rotasi tertentu (itu bisa terjadi juga, tetapi efek ini berlaku untuk setiap sistem yang mewakili rotasi, termasuk angka empat).
Anda telah menemukan fakta nyata tentang bagaimana rotasi bekerja di ruang tiga dimensi, dan itu menyimpang dari intuisi kami tentang transformasi lain seperti terjemahan:
Saat kami membuat rotasi pada lebih dari satu sumbu, hasil yang kami dapatkan bukan hanya nilai total / bersih yang kami terapkan pada setiap sumbu (seperti yang mungkin kami harapkan untuk terjemahan). Urutan di mana kita menerapkan rotasi mengubah hasilnya, karena setiap rotasi menggerakkan sumbu di mana rotasi berikutnya diterapkan (jika berputar tentang sumbu lokal objek), atau hubungan antara objek dan sumbu (jika berputar tentang dunia kapak).
Perubahan hubungan sumbu dari waktu ke waktu dapat membingungkan intuisi kita tentang apa yang harus dilakukan oleh setiap sumbu. Secara khusus, kombinasi tertentu dari rotasi yaw dan pitch memberikan hasil yang sama dengan rotasi roll!
Anda dapat memverifikasi bahwa setiap langkah berputar dengan benar tentang sumbu yang kami minta - tidak ada kesalahan mesin atau artefak dalam notasi kami yang mengganggu atau menebak-nebak input kami - sifat putaran (atau hyperspherical / angka empat) rotasi hanya berarti transformasi kami "bungkus sekitar "satu sama lain. Mereka mungkin ortogonal secara lokal, untuk rotasi kecil, tetapi ketika mereka menumpuk kita mendapati mereka tidak ortogonal secara global.
Ini paling dramatis dan jelas untuk putaran 90 derajat seperti yang di atas, tetapi sumbu pengembara merayap di banyak rotasi kecil juga, seperti yang ditunjukkan dalam pertanyaan.
Jadi, apa yang kita lakukan?
Jika Anda sudah memiliki sistem rotasi pitch-yaw, salah satu cara tercepat untuk menghilangkan roll yang tidak diinginkan adalah mengubah salah satu rotasi untuk beroperasi pada sumbu transformasi global atau induk alih-alih sumbu lokal objek. Dengan begitu Anda tidak bisa mendapatkan kontaminasi silang di antara keduanya - satu sumbu tetap benar-benar terkontrol.
Berikut urutan pitch-yaw-pitch yang sama yang menjadi gulungan pada contoh di atas, tapi sekarang kita menerapkan menguap kita di sekitar sumbu Y global alih-alih objek
Jadi kita dapat memperbaiki kamera orang pertama dengan mantra "Pitch Locally, Yaw Globally":
void Update() {
float speed = lookSpeed * Time.deltaTime;
transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f, Space.World);
transform.Rotate(-Input.GetAxis("Vertical") * speed, 0f, 0f, Space.Self);
}
Jika Anda menambah rotasi Anda menggunakan perkalian, Anda akan membalik urutan kiri / kanan dari salah satu perkalian untuk mendapatkan efek yang sama:
// Yaw happens "over" the current rotation, in global coordinates.
Quaternion yaw = Quaternion.Euler(0f, Input.GetAxis("Horizontal") * speed, 0f);
transform.rotation = yaw * transform.rotation; // yaw on the left.
// Pitch happens "under" the current rotation, in local coordinates.
Quaternion pitch = Quaternion.Euler(-Input.GetAxis("Vertical") * speed, 0f, 0f);
transform.rotation = transform.rotation * pitch; // pitch on the right.
(Urutan spesifik akan tergantung pada konvensi multiplikasi di lingkungan Anda, tetapi kiri = lebih global / kanan = lebih lokal adalah pilihan umum)
Ini sama dengan menyimpan total yaw total dan pitch total yang Anda inginkan sebagai variabel float, kemudian selalu menerapkan hasil bersih sekaligus, membangun satu angka empat orientasi baru atau matriks dari sudut-sudut ini saja (asalkan Anda tetap totalPitch
dijepit):
// Construct a new orientation quaternion or matrix from Euler/Tait-Bryan angles.
var newRotation = Quaternion.Euler(totalPitch, totalYaw, 0f);
// Apply it to our object.
transform.rotation = newRotation;
atau setara ...
// Form a view vector using total pitch & yaw as spherical coordinates.
Vector3 forward = new Vector3(
Mathf.cos(totalPitch) * Mathf.sin(totalYaw),
Mathf.sin(totalPitch),
Mathf.cos(totalPitch) * Mathf.cos(totalYaw));
// Construct an orientation or view matrix pointing in that direction.
var newRotation = Quaternion.LookRotation(forward, new Vector3(0, 1, 0));
// Apply it to our object.
transform.rotation = newRotation;
Menggunakan pemisahan global / lokal ini, rotasi tidak memiliki kesempatan untuk berkembang dan mempengaruhi satu sama lain, karena mereka diterapkan pada set sumbu independen.
Gagasan yang sama dapat membantu jika itu adalah objek di dunia yang ingin kita putar. Untuk contoh seperti bola dunia, kita sering ingin membalikkannya dan menerapkan menguap secara lokal (sehingga selalu berputar di sekitar kutubnya) dan melenggang secara global (sehingga mengarah ke / menjauh dari pandangan kita, daripada ke / jauh dari Australia , dimanapun itu menunjuk ...)
Keterbatasan
Strategi hibrid global / lokal ini tidak selalu memperbaiki yang tepat. Misalnya, dalam game dengan penerbangan 3D / berenang, Anda mungkin ingin dapat menunjuk lurus ke atas / lurus ke bawah dan masih memiliki kontrol penuh. Tetapi dengan pengaturan ini Anda akan menekan kunci gimbal - sumbu yaw Anda (global atas) menjadi sejajar dengan sumbu gulungan Anda (penerusan lokal), dan Anda tidak memiliki cara untuk melihat ke kiri atau ke kanan tanpa memutar.
Yang dapat Anda lakukan sebagai gantinya dalam kasus-kasus seperti ini adalah menggunakan rotasi lokal murni seperti yang kami mulai dengan pertanyaan di atas (sehingga kontrol Anda merasakan hal yang sama di mana pun Anda melihat), yang pada awalnya akan membiarkan beberapa gulungan masuk - tetapi kemudian kami memperbaikinya.
Misalnya, kita dapat menggunakan rotasi lokal untuk memperbarui vektor "maju" kami, lalu gunakan vektor maju itu bersama-sama dengan vektor "naik" referensi untuk membangun orientasi akhir kita. (Menggunakan, misalnya, metode Quaternion.LookRotation Unity , atau secara manual membangun matriks ortonormal dari vektor-vektor ini) Dengan mengendalikan vektor ke atas, kita mengontrol gulungan atau pelintiran.
Untuk contoh penerbangan / renang, Anda akan ingin menerapkan koreksi ini secara bertahap dari waktu ke waktu. Jika terlalu tiba-tiba, tampilan bisa bergerak dengan cara yang mengganggu. Sebagai gantinya, Anda dapat menggunakan vektor pemain saat ini dan mengisyaratkannya ke arah vertikal, frame-by-frame, sampai tampilan mereka keluar. Menerapkan ini selama belokan kadang-kadang bisa lebih sedikit mual daripada memutar kamera saat kontrol pemain idle.