Korábban megismerkedtünk már néhány vektor művelettel (2D és 3D vektorok ) és láttuk, hogy a teljes játékfejlesztés ezen vektorok körül forog. Most nézzünk át pár újabb hasznos függvényt!
A visszaverés / visszapattanás
Ahogy a neve is mutatja ez a művelet, azt mondja meg, hogy merre pattanna egy fénysugár egy megadott normálvektorú felületről
A művelet 2 és 3 dimenzióban is értelmezett
Vector3 inDirection; // A bemenő sugár iránya
Vector3 surfaceNormal; // A felület normálvektora
Vector3 reflected = Vector3.Reflect(inDirection, surfaceNormal);
Vektor szorzás
Korábban megismertünk már szorzás műveletet vektoron, ám ekkor egy vektort szoroztunk egy skalárral. Most nézzük meg milyen egyéb definiált “szorzásművelet” létezik a vektorokon:
Magyer név | Angol név | Bemenetek | Kimenet |
Szorzás skalárral | Scalar Multiplication | Skalár, Vector | Vector |
Skaláris szorzás | Dot product | 2 Vector | Skalár |
Vektoriális szorzás | Cross product | 2 Vector | Vector |
Elemenkénti szorzás | Hadamard product | 2 Vector | Vector |
Dot Product - Skaláris szorzás
A Dot Product neve onnan szármozik, hogy a pont operátorral szoktuk jelölni a matematikában, szemben a skaláris szorzással, amikor a kereszt szimbólumot használjuk:
Dot product:
Cross Product:
.
A fenti jelöléstan matematikában használatos, Unity-ben csak a már megismert skalárral történő szorzásra van dedikált operátor: *
, helyette egy függvényhíváson keresztül érhető el a művelet.
A magyar “Skalár szorzás” név pedig a függvény kimenetére utal, ami egy skalár. Tehát ha két vektoron skaláris szorzást hajtunk végre, egy számot kapunk.
Kétféle módin is kiszámolható ez a szám:
Ahol és vektorok,
a vektor egyes komponensei,
és a vektorok abszolút értékei (hosszai) (magnitude)
pedig a a két vektor által bezárt szög.
Ebből felírható tehát:
Ez azért hasznos, mert pedig megfelel a vektor vektorra történt vetítésével, és így gyorsan megkaphatjuk a vetített vektor hosszát.
Látsd a képen:
- Két vektor közti szög meghatározása
- Egy vektor vetítésre egy másikra
A skaláris szorzás működik 2D és 3D vektoron is.
Két vektor közti szög
A fentiek alapján:
Vector2 a, b;
// ...
float dot = Vector3.Dot(a, b)
float cosAngle = dot / (a.magnitude * b.magnitude);
float angleInRad = Mathf.Acos(cosAngle);
float angleInDeg = Mathf.Rad2Deg * angleInRad;
Mindez azonban másik Unity függvényekkel egy sorban is elvégezhető:
float angleInDeg = Vector2.Angle(a2, b2);
float angleInDeg = Vector3.Angle(a3, b3);
// Az eredmény mindig 0 és 180 közt lesz
// Eredmény fokban
float angleInDeg = Vector2.SignedAngle(a2, b2);
float angleInDeg = Vector3.SignedAngle(a3, b3);
// Szög óramutató járása szerint a-ból b-be értendő
// Az eredmény mindig -180 és 180 közt lesz
// Eredmény fokban
// Például
Vector2 a = Vector2.up;
Vector2 b = Vector2.right;
float angle1 = Vector2.Angle(a, b); // 90°
float angle2 = Vector2.Angle(b, a); // 90°
float signedAngle1 = Vector2.SignedAngle(a, b); // 90°
float signedAngle2 = Vector2.SignedAngle(b, a); // -90°
Egy vektor vetítésre egy másikra
A fentiek alapján:
Vector2 a, b;
// ...
float dot = Vector3.Dot(a, b)
float projectedLength = dot / b.magnitude;
Vector2 projectedVector = projectedLength * b.normalized;
Mindez azonban másik Unity függvényekkel egy sorban is elvégezhető:
// "a" vetítése "b"-re
Vector2 projected = Vector2.Project(a2, b2); // b2 vektor hossza nem számít
Vector3 projected = Vector3.Project(a3, b3); // b3 vektor hossza nem számít
Skaláris szorzás haszna
Unity-ben tehát van beépített függvény vektorok közti bezárt szög megkapására és vetítésre is. Miért mondtam akkor el, hogyan működik a dot product. Egyfelől azért, mert ez mindig hasznos informatikai-matematikai tudás, a beépített megoldások mindegyike is a háttérben garantáltan skaláris szorzást használ. Másfelől azért, mert a művelet ismeretével erőforrás-kritikus esetekben sokat optimalizálhatunk.
Bezárt szög megállapítása
Ha két vektor közti szögnek csak a koszinusza érdekel vagy a radiánban vett értéke, akkor érdemes lehet csak dot product-ot használni.
Előbbi esetre példa lehet, ha csak azt akarjuk tudni, hogy a bezárt szög hegyes vagy tompa.
Vector3 forward = transform.TransformDirection(Vector3.forward);
Vector3 toOther = otherTransform.position - transform.position;
if (Vector3.Dot(forward, toOther) < 0)
Debug.Log("The other transform is behind me!"); //Tompaszög: Mögöttem van
Vetítés
Vetítés optimalizálható, ha a vektor amire vetítünk már eleve egy egységvektor (1 hosszú vektor).
Vector2 a, b;
// ...
float dot = Vector3.Dot(a, b)
float bMagnitude = b.magnitude;
Vector2 projectedVector = b * dot / (bMagnitude * bMagnitude);
// Tehát ha tudjuk, hogy "b" egységvektor,
// akkor a magnitude-del történő osztás elhagyható:
projectedVector = b * dot;
Emlékezzünk, skalár szorzás kiszámolható egyszerűen float összeadás és szorzás műveletekkel.
Tehát így nagyon olcsón el tudtuk végezni a vetítést. A Vector2.Project
és Vector3.Project
ekkor jóval lassabb lenne.
Cross Product - Vektoriális szorzás
A vektoriális szorzás csak 3D-ben értelmezett. Két 3D vektor bemenete van és egy 3D vektor kimenete. A vektoriális szorzás azt a vektort adja vissza, ami merőleges mindkét bemenetre, paraméter vektorra.
A két bemeneti vektor nem kell, hogy merőleges legyen, csak, hogy ne legyenek párhuzamosak.
Két irányvektor is teljesíti a fenti feltételt, hogy ezek közül melyiket adja vissza a művelet, azt a paraméterek sorrendje szabályozza. Bal kéz szabállyal gondolhatjuk végig fejben, milyen irányú vektort fog visszaadni a művelet.
Az eredményvektor hossza: , ám a legtöbb esetben ez nem érdekel minket, csak az eredmény vektor normalizáltja azaz a két paraméter vektorra merőleges irány.
Vector3 a, b;
// ...
Vector3 perpendicular1 = returnVector3.Cross(a, b).normalized;
Vector3 perpendicular2 = returnVector3.Cross(b, a).normalized;
A Cross Product a Dot Product-hoz hasonlóan szintén megkapható egyszerű float osztások és szorzások alapján, ám ennek részleteit itt nem tárgyaljuk.
Merőleges 2D vektorra
Mint láttuk, 3D vektorok esetén merőlegest a vektoriális szorzás segítségével kapunk.
Ehhez két különböző 3D-s vektorra is szükségünk van, hiszen három dimenziós térben egy irányra végtelen egyéb merőleges irány létezik. Ezzel szemben 2D síkon egy nyílhoz csak kettő egyéb merőlegest tudunk felrajzolni, az eredetihez képest 90 fokban jobbra és a 90 fokban balra mutatót.
Ezek természetesen megkaphatók lennének, ha 3D-be alakítanánk az eredeti vektort, majd azt vektoriálisan szoroznánk az előre (0,0,1)
valamit hátra (0,0,-1)
mutató irányvektorral, és végül vissza-kasztolnánk Vector2
-be. Ám kettő dimenzióban ennél sokkal egyszerűbb számítás is létezik:
Vector2 v; // Eredeti vektor
//...
Vector2 perpendicularRight = new(vy, -vx); // Elforgatva jobbra 90 fokkal
Vector2 perpendicularLeft = new(-vy, vx); // Elforgatva balra 90 fokkal
Vector2 opposite = -v; // Ellentétes irány
Hadamard product - Elemenkénti szorzás
A legegyszerűbb vektorokon végzett szorzásművelet. Egyszerűen szorozza az egyes komponenseket, a párjukkal
Vector3 a, b;
//...
Vector3 scaled = Vector3.Scale(a,b);
// Manuálisan mindez így nézne ki
Vector3 scaled = new Vector3(a.x * b.x ,a.y * b.y, a.z * b.z);
Leggyakrabban erre akkor van szűkség, ha több tengely mentén egyszerre szeretnénk méretezni egy vektort: Pl.:
Vector3 a = new Vector3(12.4f, 3.1f, 22.9f);
// Méretezés
Vector3 scaled = Vector3.Scale(a, new Vector3(5,-1,3));