Gondoljuk át, hogy Unity-ben hogyan férünk hozzá egy GameObject-hez vagy komponenshez, amire szükségünk van?
Objektumok referenciái
Lehetséges, hogy felveszünk egy [SerializeField]
mezőt a célra és manuálisan bekötjük az adott objektumot. Ez a megoldás sokszor azonban túl sok munkát igényel. Ha egy GameObject-et sok, akár több száz Objektum használ, akkor nem célravezető a használata.
Az is előfordul, hogy nem csak hogy nem kényelmes, de nem is lehetséges használni a módszert. Ha például a [SerializeField]
mező egy olyan komponensen szerepel, ami egy Prefab-on van, akkor nem tudjuk manuálisan az editor felületen elvégezni a referencia bekötést, mivel egy Prefab-ra nem lehet bekötni egy Scene-ben elhelyezett GameObject-et.
Ekkor szokás kódból megkeresni egy objektumot valami módon.
Példa lehet erre GameObject.Find()
metódus használata, ami arra való, hogy visszaadjon egy GameObject-et a betöltött Scene-ből annak neve alapján.
A néven kívül persze egyéb módok is vannak arra, hogy valamiféleképp identitást rendeljünk egy játék-objektumhoz Unity-ben, úgy hogy ezután beazonosítani vagy keresni tudjuk azt a megfelelő azonosító eszköz alapján.
Foglaljuk most össze a lehetőségeinket!
Objektum identitása
Egy Objektumnak van egy neve, egy layer-e, egy tag-je és tetszőleges számú és típusú komponense is. Ezek mindegyike együttesen határoz meg egy GameObject-et, és mindet használhatjuk arra, hogy megtaláljunk vagy azonosítsunk egy objektumot.
Azonosító | Típus | Lekérdezhető | Kereshető |
Név | string | GameObject.name | GameObject.Find(); |
Layer | int | gameObject.layer | - |
Tag | string | gameObject.tag | GameObject.FindGameObjectWithTag()
GameObject.FindGameObjectsWithTag() |
Komponensek | Referencia típusok | gameObject.GetComponent<>()
gameObject.GetComponents<>() | FindObjectOfType<>()
FindObjectsOfTyep<>() |
Bővebben ezek használatáról: Unity Objektumok referenciái, Layer, Tag és Sorting Layer
Most nézzük meg mi ennek az oka! Vegyük számba a többi módszer problémáit!
Név, Layer és Tag alapú azonosítás korlátai
- String alapú keresés
- Egy azonosító - Több felhasználás
- Egy GameObject - Egy azonosító
- Egy azonosító - Egy érték
- Rossz optimalizáció és korlátozások
Mivel a nevet és a tag-eket string összehasonlítással keresi a Unity, ez azt fogja eredményezni, hogy egy objektum vagy tag átnevezése hibás működéshez vezethet méghozzá úgy, hogy erről semmi hibaüzenetet nem kapunk.
A probléma nem csak a GameObject nevekre igaz. A Unity-ben több esetben is név szerint kell vagy lehet hivatkozni programozási elemekre és ezen string összehasonlítások mindegyikével ugyanaz a fő gond: Nem biztonságos! Törékennyé teszi a kódot.
Ennek részleteit és megoldási lehetőségeit a következő lecke tárgyalja: Objektumok azonosítása string-gel
A Layer-eket a Unity sok mindenre használja a fizika és a renderelés területén belül egyaránt. Egy beállítást több mindenre felhasználni nem egy szerencsés dolog. Azért tesz mégis így a Unity mert a layer-ek (technikai részletek miatt könnyedén és nagyon gyorsan maszkolhatók.
Egy azonosítóhoz több egymástól független felhasználási módot rendelni azonban nem szerencsés. Ha megtehetjük, ne használjunk különböző célokra egy beállítást.
Képzeljük el, hogy tag-eket akarunk használni arra, hogy jelezzük az objektumokról, hogy sebezni tudjuk-e őket. Egy másik alrendszer szintén tag-eket használ arra, hogy olyan falfelületeket jelöljünk meg, amin mászni lehet.
Mi van akkor, ha a fejlesztés egy későbbi szakaszában kitaláljuk hogy olyan óriási sebezhető ellenfeleket szeretnénk, amin fel is lehet mászni. Ezt nem tudná a jelenlegi verziója a szoftvernek támogatni hiszen egy GameObject csak egy tage-et tartalmazhat. Ekkor két alrendszert is át kéne írni.
Ugyanezt a példát elmondhattam volna a layer-ekkel vagy a GameObject-ek elnevezésével is.
A nevet, réteget és tag-et nem bővíthetjük extra beállításokkal.
Tegyük fel hogy a sebezhetőséget tag-gel jelezzük. Később kitaláljuk, hogy minden sebezhető ellenfélnek van egy maximális élete és ellenállása különböző típusú sebzések ellen. Ezeket sehol nem fogjuk tudni megadni
A nevek és a tag-ek alapján történő keresés meglepően lassú.
A layer-ek használata (maszkolása) ezzel szemben azért tud gyors lenni, mert egy réteg igazából csak egy bitet jelöl egy 32 bites számban. Emiatt maximum 32 darab külön layer létezhet egy projektben, amikből a Unity kapásból el is foglal párat. Persze ez sem tűnik nagy problémának a fejlesztés elején, de amikor félúton döbbenünk rá, hogy kezdünk kicsúszni a 32-es korlátból, az sok fejfájást és extra munkát adhat egy projekthez.
Az alternatíva: Komponensek
A legrugalmasabb módja annak, hogy Unity-ben valami egyedi identitást rendeljünk egy objektumhoz, a komponenseken keresztül történik.
Komponenst készíteni tehát még akkor is hasznos lehet, ha ez az osztály csak egy teljesen üres MonoBehaviour és semmilyen belső működése nincsen. Ezt aztán hozzá lehet adni és vagy levenni egy GameObject-ről tetszés szerint.
Így nem vagyunk lekorlátozva arra, hogy csak egy tulajdonságot társítsunk egy GameObject-hez. Ráadásul egy komponenshez egyéb beállításokat is adhatunk szerializált mezők segítségével.
A lekérdezés nem string alapú, biztonságos és optimalizált.
Kódból a FindObjectOfType<>()
és a FindObjectsOfTyep<>()
lekérdezéssel bárhonnan hozzáférhetünk az összes Scene-en belüli adott típushoz anélkül, hogy string alapú keresést végeznénk.
A komponensek azonosításra történő használatával az egyetlen korlátozás, hogy csak kódolással lehet új entitásokat létrehozni.
Egy módszer az utóbbi problémára: Tag-elés ScriptableObject-tel
Nevek, Layer-ek és Tag-ek javasolt használata
- GameObject neve
- Layer
- Tag
Minden GameObject-nek adjunk jól átgondolt, sokat mondó, beszédes, de egyszerű nevet. Ezt csak arra használjuk, hogy mi mint fejlesztők könnyedén beazonosíthassunk és keressünk az objektumok közt az Editor-felületen.
Ha valamire már felhasználja a Unity a layereket, akkor arra a célra mi is nyugodtan tegyünk így.
Erre példa lehet az, hogy mire hat egy árnyék vagy fényforrás, mit lát egy kamera és mely objektumok tudnak ütközni egymással. Ezeket mi is használjuk ki bátran.
Ezzel szemben ne hasznájunk egy layer-t saját, egyedileg definiált célra. Ne jelezzen egy réteg például sebezhetőséget vagy hogy egy objektum barát vagy ellenfél.
A Tag-ekkel ugyanez a szabály. Ne használjuk őket semmi saját célra, egyedül a már Unity által implementált feladatokra!
A tag-ek ilyen motorba épített használatára csupán két példa van: EditorOnly, és a MainCamera tag.