A Raycast-ra gondoljunk úgy, mint egy lézer célzóra. Egy adott pontból indítunk egy sugarat és egy adott irányban célzunk. A lézer mindig egy adott ponton ütközik a térben egy testtel.
Ennek szükségünk van előszöris egy új típusra, a Ray
-re, azaz sugárra. A sugár egy kiinduló pontból (origin
) és egy irányból (direction
) áll össze. Mindkettőt egy vektorral reprezentálunk. Ez a bemenete a Raycast műveletnek.
A Raycast
függvény bool típusú és azt adja meg, hogy történt-e találat. Ezen kívül kimenete még a függvénynek out
paraméterrel egy RaycastHit típusú gyűjtő objektum, ami sok információt foglal magába a “találatról”. Mindjárt megnézzük miket.
Ha szeretnéd átnézni, hogy működik az out
paraméter, itt volt szó róla bővebben:
Függvények több kimenő adattal
Most végezzük el a Raycast műveletet. Ehhez egy új komponenst hozok most létre, amiben nem csak elvégzem a sugárvetést, de ki is rajzolom az eredményt gizmókkal:
using UnityEngine;
public class RayCaster : MonoBehaviour
{
void OnDrawGizmos()
{
Vector3 origin = transform.position; // A sugár kezdőpontja
Vector3 direction = transform.forward; // A sugár iránya
Ray ray = new(origin, direction); // Sugár
bool hit = Physics.Raycast(ray, out RaycastHit hitInfo); // A raycast elvégzése
// Gizmóval kirajzolom a sugarat:
if (hit) // Ha van találat, akkor
{
Vector3 hitPosition = hitInfo.point; // találat pontja
Gizmos.color = Color.green; // ZÖLD színnel rajzolunk
Gizmos.DrawLine(ray.origin, hitInfo.point); // sugarat
Gizmos.DrawSphere(hitInfo.point, 0.1f); // és a találat pontja
}
else // Ha nincs találat, akkor
{ // PIROS színnel rajzolunk sugarat
Gizmos.color = Color.red; // 1000 egység távolságig
Gizmos.DrawLine(ray.origin, ray.origin + ray.direction * 1000f);
}
}
}
2D esetben egy kicsit máshogy működik a Raycast hívása. Ott nem kell out
paramétert használni. Helyette a függvény magával a RaycastHit2D
típussal tér vissza és ez az objektum kasztolható át implicit módon bool-ba. Ez a adja meg, hogy történt-e találat. Emellett egy Ray
helyett két Vector2
vár, az origin-t és a direction-t.
Vector2 origin;
Vector2 direction;
RaycastHit2D hitInfo = Physics2D.Raycast(origin, direction); // A raycast elvégzése
if (hitInfo) // A 2D Raycast Hit átkasztolható implicit módon bool-ba
{
// ...
}
Raycast paraméterei
Extra paraméterként több mindenn megadható, amivel módosítani lehet a műveletet. Hogy a paraméterek milyen sorrendben követik egymást, arról mindig tájékozódhatsz a Unity dokumentációban és a fejlesztőkörnyezeteden belül.
distance
: Minden raycast-olásnál megadható egy maximális távolság. Ezen túl nem regisztrál találatot a rendszer.layerMask
: Megadható egy is LayerMask, amin beállítható azon rétegek, amik közt a keresést végezzük: Bővebben a rétegekről és a maszkolásról: Layer, Tag és Sorting LayerminDepth
,maxDepth
(csak 2D-ben): Ahogy a 2D fizika minden része, így a Raycast is csak x és z koordináták mentén zajlik. Lehet viszont kiszűrni olyan collidereket, amik bizonyos z értéken kívül vannak.ContactFilter2D
(csak 2D-ben): Megadható egy olyan összetett objektum is paraméterként, amiben sok szűrési beállítás megadható együtt.- Fentiek és a később következő beállítások mindegyike.
.minNormalAngle
,.maxNormalAngle
: Minimális és maximális beesési szög a talált pontjában.
A következőket nem paraméterként kell megadni, hanem egy globális változót kell átállítani, amik Project Settings-ben is elérhető Physic a és
Physics2D.queriesHitTriggers
,Physics.queriesHitTriggers
: Eltalálhat-e trigger-eket?Physics2D.queriesStartInColliders
,Physics.queriesHitBackfaces
: Eltalálhatja-e azt az objektumot, amin belülről indult a Raycast. 3D-ben ez csak a MeshCollider-rel működik, 2D-ben minden fajta Collider-rel.
Kiindulópont (origin
), irány (direction
) és max hossz (distance
) paraméterek helyett használható a kezdő és a végpont megadása is. Ekkor nem a Raycast
, hanem a Linecast
függvényt kell hívni. Egyébként teljesen egyezik a két művelet.
Több találati pont lekérése
2D-ben, és 3D-ben is van lehetőség arra, hogy ne csak egy találati pontot kapjunk vissza a Raycast-ból, hanem egy egész tömbjét a RaycastHit2D
és RaycastHit
objektumoknak. Ehhez a RaycastAll
metódust kell hívni.
RaycastHit[] hits = Physics.RaycastAll(ray); // A raycast elvégzése
foreach (var RaycastHit hit in hits)
{
// ...
}
Egy másik módja is van annak, hogy találatok egy egész sorozatát lekérjük. Ez a RaycastNonAlloc
metódus. Ennek a tömböt, amibe a találatok kerülnek paraméterként kell beadni és a függvény ezt fogja feltölteni a 0-ás indextől indulva. A visszatérése a függvények egy int, ami megadja, hogy hány valódi találat történt.
A RaycastNonAlloc
metódus pontosan ugyanarra való mint a RaycastAll
. A különbség az, hogy míg a RaycastAll
-t intuitívabb és egyszerűbb használni, addig cserében a RaycastNonAlloc
optimálisabb.
RaycastHit[] hits = new RaycastHit[10]; // Maximum annyi találatot tudunk így
// lekérni ahány elmű a tömb.
void Update()
{
Vector3 origin = transform.position; // A sugár kezdőpontja
Vector3 direction = transform.forward; // A sugár iránya
Ray ray = new(origin, direction); // Sugár
int hitCount = Physics.RaycastNonAlloc(ray, hits); // A raycast elvégzése
for (int i = 0; i < hitCount; i++)
{
RaycastHit hitInfo = hits[i];
// ...
}
}
Mivel a tömbök referenciatípusok, ezért mindig memóriaallokálás történik akkor, mikor egyet létrehozunk. Ez elkerülhető úgy hogy ugyanazt az egy tömböt töltjük fel újra és újra.
A Raycast eredménye: RaycastHit / RaycastHit2D
Mint láthattuk, a RaycastHit tartalmazza a találat pozícióját. Most nézzük meg az összes többi kinyerhető információt is:
.point
: A találat pozíciója világ-koordinátarendszerben.transform
/.collider
/.rigidbody
/.articulationBody
(csak 3D-ben): Lekérhetők az eltalált objektum különböző komponensei.distance
: A találat távolsága a sugár kiindulópontja és a találat pontja közt.fraction
(csak 2D-ben): A teljes távolság mely.normal
: A normálvektor a találat pontjában.textureCoord
,.textureCoord
,.lightmapCoord
(mind csak 3D-ben): Különböző textúrákhoz tartozó UV koordináták.triangleIndex
,.barycentricCoordinate
(mind csak 3D-ben): Egyéb információk arról, hogy a Mesh Collider mesh-ének mely háromszögét találtad el és annak is mely pontját.centeroid
(csak 2D-ben): A ShapeCast-nál hasznos. Ugyanezen leckén belül később foglalkozunk vele…
Egy 3D felület vagy 2D görbe normálvektora a felületre vagy görbére merőleges vektor. A normálvektor hossza általában 1 (, de ez definíciótól függő). Ha a felületnek van elülső és hátulsó oldala, akkor a Normál vektor kifelé a elülső oldal irányába mutat.
Egy textúra egy pontjának meghatározására szolgáló számpár. A számpár egy 2D koordináta, melyen az U nevet viseli a vízszintes és V nevet a függőleges tengely.
A koordinátarendszerben minden tengely 0 és 1 közt értelmezett: U = 0: textúra bal oldala, U = 1: textúra jobb oldala, V = 0: textúra alja, V = 1: textúra teteje.
ShapeCast
A Raycast olyan mintha egy pontszerű lövedéket lőnénk ki és megnéznénk, hogy mit talál el és hol. Emellett lehetőségünk van a fenti hasonlatot tovább görgetve kiterjedéssel rendelkező alakokat is “kilőni”. Ezen alakok 2D-ben a kör, téglalap és a 2D-s kapszula, 3 dimenzióban pedig a gömb, téglatest és a 3D-s kapszula.
Physics2D.CircleCast
Physics2D.BoxCast
Physics2D.CapsuleCast
Physics.SphereCast
Physics.BoxCast
Physics.CapsuleCast
Egy alakzat kasztolása esetén a korábbi paramétereken felül még le kell írni a megfelelő formát is. Az adott idomtól függő, hogy ez milyen adatokat jelent. A paraméterezéseket sosem kell fejből megjegyezni. Mindenki továbbra is támaszkodjon ebben a Unity dokumentációra és a kódszerkesztőjére.
A Shapecast a Raycast-hoz hasonlóan több verzióban létezik, valamelyik csak egy eredményt ad vissza, de van amelyik többet is. Pl.: CapsuleCastNonAlloc
.
A Shapecast kimenete ugyanúgy egy vagy több RaycastHit
vagy RaycastHit2D
.
A RaycastHit2D
-n belül van egy adat, ami csak ShapeCast esetén értelmezhető, ez a centeroid.
.point
: ShapeCast esetén az alakzat és az eltalált collider találkozási pontja..centeroid
(csak 2D-ben): A ShapeCast-nál az alakzat középpontja a találatnál..distance
: A találat távolsága a sugár kiindulópontja és acenteroid
közt.
A 3D RaycastHit
nem tartalmaz centeroid
adatot. (Hogy miért nem, az jó kérdés.) Viszont az ekkor is kiszámolható, a sugárból és a találat távolságából.
Vector3 centroid = ray.origin + ray.direction.normalized * raycastHit.distance;
Kattintás és Raycast
A Raycast egy gyakori felhasználása, hogy a kamera pozíciójából lövünk sugarat a kurzor pozíciója irányába. Ezáltal megmondható, melyik fizikai test felett áll az egér és hol érne hozzá.
Ezt a sugárvetést ugyanúgy kell elvégezni, ahogy azt korábban vettük. A Cast-hoz szükséges Ray előállításához azonban ismerni kell a kamera referenciáját. A sugár legegyszerűbben a kamera ScreenPointToRay
metódusával készíthető el.
if (Input.GetMouseButtonDown(0)) // Kattintáskor
{
//Ray létrehozása a kattintás helyéből:
Ray cursorRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(cursorRay, out RaycastHit hit)) // Ha van találat...
{
Debug.Log($"Clicked on: {hit.collider.name} at the position: {hit.point}.");
}
}