Зеркальні дзеркала, що обертаються


9

Я обертаю свого ігрового персонажа, щоб спостерігати за ціллю, використовуючи наступний код:

        transform.rotation = Quaternion.Slerp(startQuaternion, lookQuaternion, turningNormalizer*turningSpeed/10f)

startQuaternion - поточне обертання персонажа, коли задано нову ціль.

lookQuaternion - це напрямок, на який повинен дивитися персонаж, і він встановлений так:

    destinationVector = currentWaypoint.transform.position - transform.position;
    lookQuaternion = Quaternion.LookRotation(destinationVector, Vector3.up);

turnNormalizer просто Time.deltaTimeзбільшується і turningSpeedє статичним значенням, заданим у редакторі.

Проблема полягає в тому, що, хоча персонаж повертається так, як слід, більшу частину часу, у нього виникають проблеми, коли він повинен робити близько 180 градусів. Тоді він часом тремтить і дзеркально обертає:

введіть тут опис зображення

У цьому погано намальованому зображенні персонаж (праворуч) починає повертатися до кола зліва. Замість того, щоб просто повернути ліворуч або праворуч, він починає цей "дзеркальний танець":

  1. Він починає обертатись до нового облицювання
  2. Потім він раптово забивається під тим же кутом, але з іншого боку і продовжує обертатися

Робить це "дзеркальне відображення" так довго, поки не дивиться на ціль. Це справа з кватерніонами, сперпінгами / лерпінгами чи чимось іншим?

EDIT1: Мабуть, проблема не виникає внаслідок самого обертання. Більш ймовірною проблемою є те, що персонаж рухається в бік обличчя під час обертання. Оскільки обмеження кута між обличчям та ціллю, коли символу дозволяється рухатися, зменшується і часом виключає обертання / дзеркальне обертання.

Це, звичайно, викликає більше питань, чому персонаж не може одночасно рухатися і обертатися без проблем? Код, що використовується для руху:

transform.Translate(Vector3.forward * runningSpeed/10f * Time.deltaTime);

Чи оновлюється "lookQuaternion" кожен кадр? Якщо так, то мій припущення полягає в тому, що деякі невеликі неточності призводять до свого роду тремтіння, коли гравець "переоцінює" поворот обертання, і тому новий напрямок, який слід шукати, знаходиться лише з іншого боку "прямо позаду" ... .
Стівен Стадницький

Відповіді:


5

Кожна орієнтація в тривимірному просторі може бути представлена ​​двома окремими кватерніонами qі -q(за компонентами заперечується q). Наприклад, орієнтація, представлена ​​матрицею ідентичності 3x3, Iможе бути представлена ​​двома кватерніонами:

 q:  { 0,  0,  0},  1
-q:  {-0, -0, -0}, -1

Обидва представляють однакову орієнтацію в тривимірному просторі, їх точковий добуток рівно -1, кожен з них лежить в іншому півкулі, точно на протилежних сторонах гіперсфери.

Результат, який ви спостерігаєте, коли серп займає обертання довшої дуги, - це коли сперпінг між двома кватерніонами, які не лежать в одній півкулі. Якщо це трапиться, просто заперечте одну з них перед тим, як їх увімкнути, тоді серп займе коротшу дугу.

Точковий продукт - це простий інструмент для з'ясування того, що трапляється. Якщо крапковий добуток обох кватерніонів менший, ніж 0вони не лежать в одній півкулі. Тож якщо крапковий добуток обох менший 0, просто компонент заперечує інший кватерніон перед тим, як їх стерпіти.


Цікаво, чи я правильно тебе зрозумів, коли я спробував це, if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Inverse(startQuaternion); }але це не виправило питання. Вибачте за неякісне форматування, я не можу приручити цей розділ коментарів.
Еса

Майже, але ні Inverse, це інша операція, ніж Negate. if(Quaternion.Dot (lookQuaternion, q) < 0) { q.x = -q.x; q.y = -q.y; q.z = -q.z; q.w = -q.w; }Я не використовую C #, але з вигляду документа, мабуть, ви також могли використовувати Negate-Function безпосередньо.
Майк Семдер

if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
Майк Семдер

Боюся, це не вирішило це питання. Я оновив питання.
Еса

Так, це, мабуть, поєднання різних помилок. Спростіть налаштування тесту, випробовуйте одну за одною, а не переклад і обертання одночасно. Спочатку переконайтеся, що одна працює, потім друга, а потім обоє разом. Спершу зосередьтеся лише на обертанні, використовуйте деякі добре відомі значення орієнтації, наприклад, починаючи з 170 градусів, рухаючись вниз до нуля, дивіться, де йде не так, і розміщуйте його тут
Maik Semder

1

Ну і ваша проблема полягає в тому, що ці два вектори, які формують ваші кватерніони, складають 180 градусів, тож питання, яке слід задати при інтерполяції, яку дугу потрібно взяти? верхня дуга чи нижня ??

Це те, що в основному викликає дзеркальне відображення, щоразу, коли ви інтерполюєте, це робить навпаки, зберігаючи кут.

За цим посиланням .

Коли тета становить 180 градусів, результат не визначений, тому що немає найкоротшого напрямку обертання, я не думаю, що ця ситуація вирішується правильно, було б краще, якщо ми виберемо якусь довільну вісь, яка є нормальною для qa чи qb, я б цінуйте будь-які ідеї, щоб найкраще це зробити.

Що вам потрібно зробити, це інтерполяція вашого початкового кватерніона з проміжним кватерніоном (щоб визначити, яку дугу взяти), а коли досягнуто, використовуйте проміжний кватерніон, щоб продовжити фактичний кватерніон цілі.


1

Проблема, яку я підозрюю, що ви бачите, не має нічого спільного з вашим кодом, а скоріше є принциповою проблемою з інтерполяцією по поверхні сфери (в даному випадку для viewDirection). Між (майже) будь-якими двома точками на кулі є одне велике коло - наприклад, довільно фіксуючи нашу "стартову" точку на північному полюсі, велике коло буде меридіаном, на якому лежить кінцева точка. Інтерполяція від однієї точки до іншої рухається по цьому великому колу.

Тепер, для більшості точок на кулі, точки поблизу відповідають сусіднім великим колам; наприклад, меридіани, на яких лежать Лос-Анджелес та Фенікс, досить близькі один до одного. Але коли точкою призначення є антипод вихідної точки - південний полюс до північного полюса початкової точки - через них не існує жодного великого кола, а натомість всі великі кола через один проходять через інший. Що ще гірше, це означає, що точки біля південного полюса - це не «більшість точок»; дві точки поруч одна з одною і обидві біля південного полюса можуть мати дивовідмінні меридіани.

Я підозрюю, що те, що ти бачиш, є проявом цієї нестабільності на меридіані - або іншими словами, дугою інтерполяції. Коли ціль погляду знаходиться безпосередньо за персонажем, то такі речі, як неточність першого порядку у вашій «інтеграції Ейлера» щодо позиції персонажа, і спосіб, який змінює напрямок погляду кадр в кадр, призведуть до цього дико. різні дуги інтерполяції від одного кадру до іншого, і це, швидше за все, призведе до "хитання", яке ви бачите.

Що стосується того, що з цим робити, найкраще, що спадає на думку, - це не перераховувати напрямок "цільового" вигляду в кожен кадр; замість цього використовуйте ту саму для проміжку декількох кадрів, потім обчисліть новий і використовуйте для декількох кадрів і т. д. Якщо потрібно, ви навіть можете інтерполювати від однієї цілі до іншої через проміжок кількох кадрів, щоб напрямок руху не надто різко змінюється.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.