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.deltaTime
zwiększany i turningSpeed
jest 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:
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”:
- Zaczyna się obracać w kierunku nowej okładziny
- 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);
Odpowiedzi:
Każda orientacja w przestrzeni 3D może być reprezentowana przez 2 odrębne ćwiartki jednostek
q
i-q
(negowane komponentowoq
). Na przykład orientacja reprezentowana przez macierz tożsamości 3x3I
może być reprezentowana przez 2 ćwiartki: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 mniejszy0
, to po prostu z punktu widzenia komponentu neguj drugi czwartorzęd przed ich przesunięciem.źródło
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.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.if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
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 .
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.
źródło
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.
źródło