Ні, це не помилка двигуна чи артефакт конкретного представлення обертання (це теж може статися, але цей ефект застосовується до кожної системи, що представляє обертання, включені кватерніони).
Ви відкрили реальний факт про те, як працює обертання в тривимірному просторі, і це відходить від нашої інтуїції щодо інших перетворень, таких як переклад:
Коли ми складаємо обертання на більш ніж одній осі, результат, який ми отримуємо, - це не просто загальне / чисте значення, яке ми застосували до кожної осі (як ми могли очікувати для перекладу). Порядок, в якому ми застосовуємо обертання, змінює результат, оскільки кожне обертання переміщує осі, на які застосовуються наступні обертання (якщо обертається навколо локальних осей об'єкта), або співвідношення між об'єктом і віссю (якщо обертається навколо світу оси).
Зміна відношень осей з часом може збити з пантелику нашу інтуїцію щодо того, що кожна вісь "повинна" робити. Зокрема, певні комбінації повороту нитки та нахилу дають такий же результат, що й обертання рулону!
Ви можете переконатись, що кожен крок обертається правильно щодо осі, про яку ми просили - у наших позначеннях немає жодних збоїв чи артефактів двигуна, які втручаються або не вгадують наш вхід - сферичний (або гіперсферичний / кватерніонний) характер обертання просто означає, що наші трансформації "обертаються навколо "один на одного. Вони можуть бути ортогональними локально, для невеликих обертів, але, оскільки вони накопичуються, ми виявляємо, що вони не є ортогональними в усьому світі.
Це найбільш драматично і зрозуміло для поворотів на 90 градусів, як і вище, але блукаючі осі повзають і через багато малих обертів, як показано в питанні.
Отже, що ми з цим робимо?
Якщо у вас вже є система повороту нахилу нахилу, один з найшвидших способів усунення небажаного валка - це змінити одне з обертів, щоб діяти на глобальних або батьківських осях перетворення замість локальних осей об'єкта. Таким чином, ви не можете отримати перехресне забруднення між двома - одна вісь залишається абсолютно контрольованою.
Ось та сама послідовність кроку нахилу, яка стала рулоном у наведеному вище прикладі, але тепер ми застосуємо наше позіхання навколо глобальної осі Y замість об'єкта
Таким чином, ми можемо виправити камеру від першої особи за допомогою мантри "Pitch Locally, Yaw Globalno":
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);
}
Якщо ви поєднуєте обертання за допомогою множення, ви перевернете лівий / правий порядок одного з множень, щоб отримати той самий ефект:
// 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.
(Конкретний порядок залежатиме від умов множення у вашому середовищі, але лівий = більш глобальний / правий = більше локальний - це загальний вибір)
Це еквівалентно збереженню чистого загального промахування та загального кроку, який ви хочете як плаваючі змінні, а потім завжди застосовувати чистий результат відразу відразу, будуючи єдиний новий орієнтаційний кватерніон або матрицю з цих кутів (за умови, що ви тримаєте totalPitch
затиснуті):
// 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;
або рівнозначно ...
// 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;
Використовуючи цей глобальний / локальний розкол, обертання не мають шансу скластись і впливати один на одного, оскільки вони застосовуються до незалежних наборів осей.
Ця ж ідея може допомогти, якщо це предмет, який ми хочемо обертати. Для такого прикладу, як земна куля, ми часто хочемо перевернути його і застосувати наше позіхання локально (так що він завжди крутиться навколо своїх полюсів) і нахилитись у всьому світі (тому він підказує до / від нашого погляду, а не до / вдалині від Австралії , куди це вказує ...)
Обмеження
Ця глобальна / локальна гібридна стратегія не завжди є правильним виправленням. Наприклад, у грі з 3D-польотом / плаванням, можливо, ви хочете мати можливість направляти прямо вгору / прямо вниз і все ще мати повний контроль. Але за допомогою цього налаштування ви потрапите на карданний замок - ваша вісь повороту (глобальна вгору) стає паралельною осі рулону (локальний вперед), і у вас немає способу дивитися ліворуч або праворуч без скручування.
Що можна зробити замість таких випадків, - це використовувати чисті локальні обертання, як ми почали з вищезазначеного питання (тому ваші елементи керування відчувають те саме, де б ви не шукали), що спочатку дозволить проскочити кочення - але потім ми виправляємо це.
Наприклад, ми можемо використовувати локальні обертання для оновлення вектора "вперед", а потім використовувати цей вектор вперед разом з опорним вектором "вгору" для побудови нашої кінцевої орієнтації. (Використовуючи, наприклад, Quaternion Unity's.LookRotation метод , або вручну побудуючи ортонормальну матрицю з цих векторів) Керуючи вектором вгору, ми керуємо рухом або скручуванням.
Для прикладу польоту / плавання, ви хочете застосовувати ці виправлення поступово з часом. Якщо він занадто різкий, погляд може відволікати відволікаючим способом. Натомість ви можете використовувати поточний вектор гравця вгору та натякнути його на вертикаль, кадр за кадром, поки їх вигляд не вирівняється. Застосовуючи це під час повороту, іноді може бути менше нудоти, ніж скручування камери, коли елементи керування програвача простоюють.