Ha lekérdezzük egy Transform komponens pozícióját és skálázását, a visszakapott érték típusa Vector3. Ha megnézzük a Transform komponens Inspector ablakát, akkor az alapján arra számítanánk, hogy az elfordulás is szintén Vector3 típusú lesz, de csalódnunk kell. A rotációt egy úgy nevezett Quaternion (Magyarul gyakran kvaternió) nevű típusú változó tárolja.
Vector3 localPosition = transform.localPosition;
Vector3 position = transform.position;
Vector3 localScale = transform.localScale;
Vector3 lossyScale = transform.lossyScale;
Quaternion localRotation = transform.localRotation;
Quaternion rotation = transform.rotation;
A Quaternionokról itt olvashattok bővebben, ám ezt kezdők számára nem feltétlenül javaslom: Quaternion-ok a 3D grafikában
A Quaternion egy bonyolult matematikai fogalom és megértése jóval nagyobb matematikai kihívást jelent, mint az eddig tárgyaltak és az ezen a kurzuson következők. Pár jó hírem is van viszont.
Először is, hogy a teljesen megértése egyáltalán nem szükséges a Unity alatti használathoz. Rengeteg eszközt fogunk kézhez kapni, amikkel a mélyebb megértés hiányában is fogjuk tudni használni a típust.
Másodszor létezik egy másik módja is az elfordulás reprezentálásra a 3D térben, ami emberi szemlélő számára sokkal átláthatóbb és megérthetőbb: Ezeket nevezzük Euler szögeknek. Ezen Euler szögek vagy más néven Euler koordináták és a Quaternionok közt pedig könnyű átjárást tesz lehetővé a Unity. Miért használunk mégis Quaternionokat akkor egyáltalán? Ezzel most ne foglalkozzunk. Ha valakit nagyon érdekel, utána tud olvasni a fent linkelt cikkben. Lényeg hogy technikai jellegű az ok.
Forgatás Euler szögekkel
Mielőtt megvizsgálnánk a Quaternionok működését, nézzük meg milyen egyéb módon ábrázolhatunk egy forgatást!
Ha egy testet elforgatunk saját (lokális)
- X tengelye körül x fokkal,
- Y tengelye körül y fokkal,
- Z tengelye körül z fokkal,
akkor egy számhármassal leírható a forgatás. A különböző tengelyek közti fokban leírt forgatást reprezentálhatjuk egy számhármassal, azaz vektorral: (x, y, z). A Unity-ben már van is erre a célra egy típus, a Vector3. Ez a bizonyos Vektor3 látszik a Unity Editorban az Inspector fülön.
Euler szögek vagy Quaternionok közt a következő módon tudunk típusokat átalakítani.
Vector3 euler = new Vector3(1, 1, 1);
Quaternion q = Quaternion.Euler(euler); // Euler -> Quaternion
Vector3 toEuler = q.eulerAngles; // Quaternion -> Euler
Az identitás quaternion
Az alapértelmezett Quaterniont identitásnak is nevezzük és könnyen lekérhető a következőképp:
Quaternion identity = Quaternion.identity;
bool testIdentity = q * identity == q; // true
Ez megegyezik a (0, 0, 0) Euler szögekkel.
Quaternionok létrehozása
Unity alatt Quaternionokat többféle módon tudunk létrehozni. Ebből néhányat mutatok be. Ez lesz a fő eszköztárunk, amire szükségünk lesz 3D elforgatésok létrehozásához:
// Euler szögekből (Ezt előbb tárgyaltuk)
Quaternion q1 = Quaternion.Euler(euler); // Euler -> Quaternion
// Egy tengelyből (Vector3 axis) és egy tengely körüli elforgatás szögből (float angle):
// Ez a függvény a identitás-t (alap elforgatást) módosítja úgy hogy azt elforgatja
// a paraméterben megadott tengely körül a paraméterben megadott szögben
Quaternion q2 = Quaternion.AngleAxis(angle, axis);
// Egy lokálisan előre mutató vektorból:
// Ebben az esetben a lokális felfelé a legközelebb lesz a globális felfelé irányhoz.
Quaternion q2 = Quaternion.LookRotation(direction);
// Egy lokálisan előre és egy felfelé mutató vektorból:
// Ebben az esetben a lokális felfelé a legközelebb lesz a beállított felfelé irányhoz.
Quaternion q4 = Quaternion.LookRotation(direction, Vector3.up);
Mindezek közül a gyakorlatban a LookRotation
a leggyakrabban használt. Mint ahogy azt a fenti kód szemlélteti, 2 változata van a függvénynek: az egy és a két paraméteres LookRotation
.
Mindkét függvény mindkét paramétere irányvektor. Az első paraméter minden esetben azt mondja meg, hogy az elkészült elforgatás melyik irányba nézzen. Más szavakkal melyik irányba álljon a Quaternion lokális előre vektora.
A fenti videóban a fej forgását a következő kódrészlet irányítja, ahol a forwardDirectionTarget
a gömb objektum Transform
-ja. Tehát a piros vonal a paraméterben megadott irányvektor.
Vector3 forward = forwardDirectionTarget.position - transform.position; // Előre irány
transform.rotation = Quaternion.LookRotation(forward);
A kétparaméteres verzióban a második paraméter, azt adja meg, hogy a forgatás felfelé vektora melyik irányba nézzen. Gondoljuk végig, ez csak akkor működhet pontosan, hogyha a két megadott vektor pontosan 90 fokot zár be egymással. Általában persze ez nem így van. Ekkor úgy áll elő a Quaternion
, hogy a második (felfelé) paraméter vektor a lehető legkisebb szöget zárja be az előállt elforgatás lokális felfelé irányával.
A fenti videóban a repülő forgását a következő kódrészlet irányítja, ahol a forwardDirectionTarget
és upDirectionTarget
a két gömb objektum Transform
-ja. Tehát a kék és a zöld vonalak a két paraméterben megadott irányvektor.
Vector3 forward = forwardDirectionTarget.position - transform.position;
Vector3 up = upDirectionTarget.position - transform.position;
transform.rotation = Quaternion.LookRotation(forward, up);
Figyeljük meg a fenti videón, hogy a repülő lokális felfelé iránya sosem egyezik meg pontosan a zöld felfelé parméterrel, de annyia közelíti azt, amennyire ez amellett lehetséges, hogy az előre irány pontos legyen!
Ha az egy paraméteres verziót használjuk, akkor a lokális felfelé irány a globális felfeléhez, azaz a (0,1,0)
vektorhoz próbál közelíteni, amennyire lehetséges. Tehát a következő két sor teljesen ekvivalens (Ugyanazt teszik)
Quaternion q1 = Quaternion.LookRotation(direction);
Quaternion q2 = Quaternion.LookRotation(direction, Vector3.up);
Mindemellett “keverhetünk” azaz súlyozva átlagolhatunk különböző Quaternion
-okat lineáris interpolációval, vagy közelíthetjük egyiket egy másikhoz fix sebességgel. Így elérhetőnk simított és egyéb speciális forgásokat is. Ezekről azonban később a következő fejezetekben olvashatsz:
Lineáris és simított interpoláció, A Towards függvények. E
Azonban, ha ezt a Quaternion
-t, amit a LookRoatation
függvénnyel előállítottunk egyből fel akarjuk használni arra, hogy azzal egy Transform
-ot (GameObject
-et) elforgassunk, azaz ezt szeretnénk, hogy egy Objektum egy másik felé nézzen, akkor ezt megtehetjük a következő egyszerűsített függvényekkel is:
Transfotm targetObject;
// ...
Vectro3 direction = targetObject.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(direction);
transform.rotation = rotation;
// A fentiek egyszerűsíthetők:
transform.LookAt(target.position); // Célpont egy helyvektor
transform.LookAt(target); // Célpont egy másik transform
// Két paraméteres verziók:
transform.LookAt(target.position, Vector3.up); // Felfelé irány: opcionális
transform.LookAt(target, Vector3.up); // Felfelé irány: opcionális
// Sőt még ez is működik: // Hasonló módon a
transform.forward = direction; // transform.right és transform.up
// is beállítható
Transform-ok folyamatos forgatása
transform.Rotate(Vector3.up, 90); // A transzform elforgatása a függőleges tengelye körül 90 fokkal
transform.Rotate(0,90,0); // Ugyanaz másként (Tengelyenként megaadva a fokokat.)
transform.Rotate(new Vector3(0,90,0)); // Ugyanaz másként
// A fenti forgatások mind lokális térben történnek, azaz a játékos saját tengelyei körül.
// Ha globális tengelyek körül szeretnénk forgatni, akkor a Space.World-öt kell utolsó paramétert megadni.
transform.Rotate(Vector3.up, 90, Space.World); // A transzform elforgatása a globális függőleges tengely körül 90 fokkal
transform.Rotate(0,90,0, Space.World); // Ugyanaz másként (Tengelyenként megaadva a fokokat.)
transform.Rotate(new Vector3(0,90,0), Space.World); // Ugyanaz másként
Ha mindezt az Update
metódusban tesszük és egyenletes mozgást szeretnénk elérni, ne feledkezzünk meg a Time.deltaTime
-mal történő szorzásról:
(Ez a példa a különböző terekben történő fogatást is szemlélteti.)
[SerializeField] float angularSpeed = 90; // Szögsebesség (fokban)
[SerializeField] Space space; // Lokális vagy globális tér
void Update()
{
float angle = angularSpeed * Time.deltaTime;
// Függőleges tengely körül forgatás:
transform.Rotate(Vector3.up, angle, space);
}
A 2D forgatás
Láttuk, hogy milyen bonyolult tud lenni 3D-ben reprezentálni egy elfordulást. Ezzel szemben 2D-ben egyszerűbb nem is lehetne. Szimplán csak egy float
szám leírhat egy 2D elfordulást általában fokban kifejezve.
Mivel a Unity egy 3D engine, ezért a 2D játékokban is az objektumok elfordulását Quaternion
-ok reprezentálják. Szerencsére az átalakítás eléggé egyszerű: Csak az Euler szögek közül a z koordinátát kell lekérni vagy állítani.
// 2D -> 3D
float rotation2D = 90; // 90°
Quaternion rotation3D = Quaternion.Euler(0, 0, rotation2D);
// 2D -> 3D
angle = rotation3D.eulerAngles.z;
A Quaternion.LookRotation
-ra nincs 2D megfelelő, pedig szükségünk lehet arra, hogy egy adott vektor irányába nézzen az objektumunk kettő dimenzióban is.
Ehhez először az Vector2
irányt egy float
típusú fokban megadott szög értékbe kell átváltani:
Vector3 localForward = Vector2.up; // A 2D alakzat előre iránya
Vector3 globalForward = Vector2.up; // Melyik irányba akarunk nézni
// A szög kiszámítása:
float rotation2D = Vector2.SignedAngle(localForward, globalForward);
// Quaternion legyártása:
transform.rotation = Quaternion.Euler(0, 0, rotation2D);
De miért kell kétfajta előre irány?
- A fenti példában a
globalForward
adja merre akarunk nézni a globális térben. - A
localForward
pedig azt mondja meg, hogy a mi az adott objektum lokális előre iránya a 2D síkon.
3D-ben ez mindig kötelezően a Vector3.forward
kellett legyen különben nem működött a Quaternion.LookRotation
helyesen.
2D-ben ez nem ennyire jól definiált. Láts a következő példán: