Przesuwne lusterka obrotowe

9

Obracam swoją postać gry, aby obserwować cel, używając następującego kodu:

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

startQuaternion to bieżąca rotacja postaci, gdy zostanie podany nowy cel.

lookQuaternion to kierunek, w którym postać powinna patrzeć i jest ustawiony następująco:

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

TurningNormalizer jest tylko Time.deltaTimezwiększany i turningSpeedjest wartością statyczną podaną w edytorze.

Problem polega na tym, że chociaż postać obraca się tak, jak powinna przez większość czasu, ma problemy, gdy musi zrobić blisko 180 stopni. Potem czasami drży i odzwierciedla obrót:

wprowadź opis zdjęcia tutaj

Na tym źle narysowanym obrazie postać (po prawej) zaczyna obracać się w kierunku koła po lewej. Zamiast po prostu skręcić w lewo lub w prawo, zaczyna się ten „lustrzany taniec”:

  1. Zaczyna się obracać w kierunku nowej okładziny
  2. Potem nagle zaskoczy pod tym samym kątem, ale z drugiej strony i obraca się

Robi to „odbicie lustrzane” tak długo, aż spojrzy na cel. Czy to coś z czwartorzędami, slerpingiem / lerpowaniem czy coś innego?

EDYCJA 1: Najwyraźniej problem nie wynika z samego obrotu. Bardziej prawdopodobne jest to, że postać obraca się w kierunku twarzy. Ograniczenie kąta między twarzą a celem, gdy postać może się poruszać, zmniejsza, a czasem eliminuje rotację drgań / odbicia lustrzanego.

To oczywiście rodzi więcej pytań, dlaczego postać nie może się poruszać i obracać jednocześnie bez problemów? Kod użyty do przemieszczenia:

transform.Translate(Vector3.forward * runningSpeed/10f * Time.deltaTime);
Esa
źródło
Czy „lookQuaternion” jest aktualizowany co klatkę? Jeśli tak, to zakładam, że niektóre małe nieścisłości prowadzą do pewnego rodzaju drgań, w których gracz „przeskakuje” rotację wyglądu, a więc nowy kierunek patrzenia jest po drugiej stronie „bezpośrednio za”… .
Steven Stadnicki

Odpowiedzi:

5

Każda orientacja w przestrzeni 3D może być reprezentowana przez 2 odrębne ćwiartki jednostek qi -q(negowane komponentowo q). Na przykład orientacja reprezentowana przez macierz tożsamości 3x3 Imoże być reprezentowana przez 2 ćwiartki:

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

Oba reprezentują tę samą orientację w przestrzeni 3D, ich iloczyn skalarny jest dokładnie -1, każdy z nich leży na drugiej półkuli, dokładnie po przeciwnych stronach hipersfery.

Rezultat, który obserwujesz, gdy slerp obraca dłuższy łuk, ma miejsce podczas przesuwania między 2 czwartorzędami, które nie leżą na tej samej półkuli. Jeśli tak się stanie, po prostu zaneguj jeden z nich, zanim zaczniesz je przesuwać, wtedy slerp przyjmie krótszy łuk.

Produkt kropkowy jest łatwym narzędziem do sprawdzenia, czy tak się dzieje. Jeśli iloczyn punktowy obu czwartorzędów jest mniejszy niż 0, wówczas nie leżą one na tej samej półkuli. Więc jeśli iloczyn punktowy obu z nich jest mniejszy 0, to po prostu z punktu widzenia komponentu neguj drugi czwartorzęd przed ich przesunięciem.

Maik Semder
źródło
Zastanawiam się, czy dobrze cię zrozumiałem, gdy próbowałem z tym, if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Inverse(startQuaternion); }ale to nie rozwiązało problemu. Przepraszam za słabe formatowanie, nie mogę chyba oswoić tej sekcji komentarzy.
Esa
Prawie, ale nie Inverse, to inna operacja niż Negate. if(Quaternion.Dot (lookQuaternion, q) < 0) { q.x = -q.x; q.y = -q.y; q.z = -q.z; q.w = -q.w; }Nie używam C #, ale z wyglądu docu wydaje się, że można również użyć funkcji Negate bezpośrednio.
Maik Semder
if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
Maik Semder
Obawiam się, że to nie rozwiązało problemu. Zaktualizowałem pytanie.
Esa
Tak, to prawdopodobnie mieszanka różnych błędów. Uprość konfigurację testu, testuj jedną rzecz naraz, a nie tłumaczenie i rotację w tym samym czasie. Najpierw upewnij się, że jedno działa, potem drugie, a następnie oba razem. Najpierw skup się tylko na rotacji, użyj dobrze znanych wartości orientacji, takich jak początek od 170 stopni, zejście do zera, zobacz, gdzie idzie źle, i opublikuj go tutaj
Maik Semder
1

Twój problem polega na tym, że te dwa wektory tworzące twoje czwartorzędy tworzą 180 stopni, więc pytanie, które należy zadać przy interpolacji, który łuk powinien przyjąć? górny łuk czy dolny?

To właśnie powoduje lustro, za każdym razem, gdy interpolujesz, odwraca się, zachowując kąt.

Według tego linku .

Gdy theta wynosi 180 stopni, wynik jest niezdefiniowany, ponieważ nie ma najkrótszego kierunku obrotu, nie sądzę, że ta sytuacja jest jeszcze obsługiwana poprawnie, byłoby lepiej, gdybyśmy wybrali jakąkolwiek dowolną oś, która jest normalna dla qa lub qb, zrobiłbym to docenić wszelkie pomysły na najlepszy sposób, aby to zrobić.

To, co musisz zrobić, to interpolować początkową ćwiartkę z pośrednią ćwiartką (w celu ustalenia, który łuk wziąć), a po osiągnięciu użyć czwartorzędu pośredniego, aby przejść do faktycznej czwartorzędu docelowego.

concept3d
źródło
1

Wydaje mi się, że problem, który widzisz, nie ma nic wspólnego z twoim kodem, ale jest podstawowym problemem z interpolacją wzdłuż powierzchni kuli (w tym przypadku dla viewDirection). Pomiędzy (prawie) dowolnymi dwoma punktami na kuli znajduje się jeden wielki okrąg - na przykład, arbitralnie ustalając nasz punkt początkowy na biegunie północnym, wielki okrąg byłby południkiem, na którym leży punkt końcowy. Interpolacja z jednego punktu do drugiego porusza się wzdłuż tego wielkiego koła.

Teraz, dla większości punktów na kuli, punkty w pobliżu odpowiadają pobliskim wielkim kręgom; na przykład południki, na których leżą Los Angeles i Phoenix, są dość blisko siebie. Ale kiedy punkt docelowy jest antypodem pierwotnego punktu - biegun południowy do bieguna północnego punktu początkowego - nie ma już ani jednego wielkiego koła przez oba, ale wszystkie wielkie koła przez jedno przechodzą przez drugie. Co gorsza, oznacza to, że punkty w pobliżu bieguna południowego nie są „większością punktów”; dwa punkty blisko siebie i oba w pobliżu bieguna południowego mogą mieć bardzo rozbieżne meridiany.

Podejrzewam, że to, co widzisz, jest przejawem tej niestabilności na południku - lub innymi słowy, łuku interpolacji. Kiedy cel patrzenia znajduje się bezpośrednio za postacią, rzeczy takie jak niedokładność pierwszego rzędu w twojej „integracji Eulera” pozycji postaci i sposób, który zmienia kierunek patrzenia klatka po klatce, doprowadzą do tych szalenie różne łuki interpolacyjne z jednej klatki do drugiej, a to prawdopodobnie doprowadzi do „zataczania się”, które widzisz.

Jeśli chodzi o to, co z tym zrobić, najlepszą rzeczą, jaka przychodzi mi do głowy, jest nie przeliczanie kierunku spojrzenia „docelowego” w każdej klatce; zamiast tego użyj tego samego dla rozpiętości kilku klatek, a następnie oblicz nową i użyj tego dla kilku klatek itp. W razie potrzeby możesz nawet interpolować od jednego celu do drugiego na rozpiętości kilku klatek, tak aby kierunek ruchu nie zmienia się zbyt gwałtownie.

Steven Stadnicki
źródło