Mnożenie
Przynajmniej jeśli chodzi o implementację Quaternions przez Unity, kolejność mnożenia opisana w pytaniu jest nieprawidłowa. Jest to ważne, ponieważ obrót 3D nie jest przemienny .
Tak więc, jeśli chcę obrócić obiekt, rotationChange
zaczynając od jego currentOrientation
, zapisałbym go w następujący sposób:
Quaternion newOrientation = rotationChange * currentOrientation;
(tj. Transformacje układają się w górę w lewo - tak samo jak w konwencji macierzy Unity. Obrót w prawo jest stosowany najpierw / na „najbardziej lokalnym” końcu)
A gdybym chciał przekształcić kierunek lub wektor przesunięcia przez obrót, zapisałbym to w ten sposób:
Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;
(Unity wygeneruje błąd kompilacji, jeśli zrobisz coś przeciwnego)
Mieszanie
W większości przypadków można uniknąć rotacji Lerping. Wynika to z faktu, że kąt zastosowany „pod maską” w ćwiartce jest o połowę mniejszy od obrotu, co czyni go znacznie bliższym liniowemu przybliżeniu Lerpa niż coś w rodzaju Matrycy (która ogólnie nie będzie dobrze Lerpem!). Zapoznaj się z około 40 minutami tego filmu, aby uzyskać więcej wyjaśnień .
Jedynym przypadkiem, gdy naprawdę potrzebujesz Slerpa, jest to, że potrzebujesz stałej prędkości w czasie, na przykład interpolacji między klatkami kluczowymi na osi czasu animacji. W przypadkach, w których zależy ci tylko na tym, aby wynik był pośredni między dwoma wejściami (jak mieszanie warstw animacji), zwykle Lerp działa całkiem dobrze.
Co jeszcze?
Iloczyn skalarny dwóch kwaternionów jednostkowych daje cosinus kąta między nimi, dzięki czemu można korzystać z iloczynu skalarnego jako miara podobieństwa jeśli trzeba porównać obroty. Jest to jednak trochę niejasne, więc dla bardziej czytelnego kodu często używałbym Quaternion.Angle (a, b) , co wyraźniej wyraża, że porównujemy kąty w znanych jednostkach (stopniach).
Tego rodzaju metody wygody, które Unity zapewnia dla Quaternions, są bardzo przydatne. W prawie każdym projekcie używam tego co najmniej kilka razy :
Quaternion.LookRotation(Vector3 forward, Vector3 up)
Tworzy to kwaternion, który:
- obraca lokalną oś z +, aby wskazywać dokładnie wzdłuż
forward
argumentu wektorowego
- obraca lokalną oś y +, aby wskazywała jak najbliżej
up
argumentu wektorowego, jeśli jest podany, lub (0, 1, 0)
jeśli jest pominięty
Powodem, dla którego „wzrost” jest „jak najbliższy”, jest to, że system jest zawyżony. Stawianie forward
czoła z + powoduje wykorzystanie dwóch stopni swobody (tj. Odchylenie i skok), więc mamy tylko jeden stopień swobody (przechylenie).
Dość często szukam czegoś o przeciwnych właściwościach dokładności: chcę, aby lokalny y + wskazywał dokładnie wzdłuż up
, a lokalny z + zbliżał się tak blisko, jak to możliwe forward
z pozostałą swobodą.
Pojawia się to na przykład podczas próby utworzenia ramki współrzędnych względem kamery do wprowadzania ruchu: chcę, aby mój lokalny kierunek w górę pozostawał prostopadły do podłogi lub nachylonej powierzchni normalnej, więc mój wkład nie próbuje tunelować postaci w teren lub lewitować ich z tego.
Możesz to również uzyskać, jeśli chcesz, aby obudowa wieży skierowana była w stronę celu, bez odrywania się od korpusu czołgu podczas celowania w górę / w dół.
W tym celu możemy zbudować naszą własną funkcję wygody, używając LookRotation
do podnoszenia ciężkich przedmiotów:
Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);
return rotateZToUp * rotateYToZ;
}
Tutaj najpierw obracamy lokalne y + do z +, a lokalne z + do y-.
Następnie obracamy nowe z + do naszego kierunku w górę (więc wynik netto to lokalne y + punkty bezpośrednio wzdłuż exactUp
), a nowy y + jak najbliżej zanegowanego kierunku do przodu (więc wynik netto to lokalne punkty + + jak najbliżej) approximateForward
)
Inną przydatną metodą wygody jest Quaternion.RotateTowards
, z której często korzystam:
Quaternion newRotation = Quaternion.RotateTowards(
oldRotation,
targetRotation,
maxDegreesPerSecond * Time.deltaTime
);
To pozwala nam zbliżać się targetRotation
ze stałą, kontrolowaną prędkością bez względu na liczbę klatek na sekundę - ważne dla rotacji, które wpływają na wynik / uczciwość mechaniki rozgrywki (np. Obracanie ruchu postaci lub śledzenie wieżyczki na graczu). Naiwny Lerping / Slerping w tej sytuacji może łatwo prowadzić do przypadków, w których ruch staje się szybszy przy wysokich prędkościach klatek, co wpływa na równowagę gry. (Nie oznacza to, że te metody są niewłaściwe - istnieją sposoby na ich prawidłowe użycie bez zmiany uczciwości, wymaga jedynie opieki. RotateTowards
Daje wygodny skrót, który się tym za nas opiekuje)
n
różne orientacje (postawy, pozy itp.). Następnie możesz je uśrednić za pomocą wag, skutecznie uogólniając slerp / lerp. Możesz także przekształcić ćwiartkę w wirnik, co jest równoważne zastosowaniu prędkości kątowej przez określony czas do sztywnego korpusu. Dlatego też można opisać całkowanie prędkości kątowej również z czwartorzędami. Możesz także oszacować, jak różne są dwie orientacje (obliczyć długość łuku rozpiętego przez dwa ćwiartki na hipersferze).