Egy összetett GameObject sok-sok gyerek GameObject-ből állhat bonyolult hierarchiában. Ezen belül egy gyerek GameObject-et sok komponens és komponensenként számtalan beállítás ír le.
Tételezzük fel, hogy ez a bizonyos bonyolult GameObject a játékunkban egy több helyen előforduló dolgot ír le, mint például egy gyakori ellenfelet. Akár többszáz helyen előfordulhatnak ezek a GameObject-ek elosztva sok Scene-ben. Ha ezen ellenfelek mindegyikét szimpla másolással hoztuk létre egy eredetiből, akkor a legminimálisabb változtatást a GameObject-en többszáz helyen kéne egymás után megtennünk. Ez persze nem egy hosszú távon működőképes hozzáállás.
Segítségünkre vannak ilyen helyzetekben a Prefab-ok. Egy-egy GameObject-ből asset fájlt készíthetünk annak minden beállításával, gyerek objektumával és a gyerek objektumok beállításaival együtt. Ezek a prefabok.
A prefab egy file, ami egy GameObject-et reprezentál gyerekeivel, komponenseivel és beállításaival együtt.
A prefabok lehetővé teszik a fejlesztők számára a játék elemeinek előre elkészített, újrahasznosítható változatainak létrehozását és használatát.
Prefab létrehozása és példányosítása
Prefab-ot úgy hozhatunk létre egy már létező GameObject-ből, hogy a Hierarchy ablakból Drag and Drop módszerrel áthúzzuk azt az Asset-ek közé a Project ablakra.
Ezután ha a filet bárhova visszahúzzuk a Scene-be, ott a prefab egy példánya fog szerepelni. Amikor egy scene-ben egy Prefab példány van jelen, akkor annak beállításai nem a Scene részei többé.
Ha egy Prefab-ból példányt hozunk létre, akkor nem másoljuk a GameObject adatait a Scene-be, csak a referenciát az adott Prefab-ra. Az adatokat a Prefab-ról tehát maga a Prefab file tárolja nem a Scene. Egy Scene-ben csak a referencia szerepel a prefab file-ra.
Így, ha bármi változik a Prefab fájlbon, akkor minden Scene-beli példány módosulni fog vele együtt egyszerre és egységesen.
Egyedi módosítások: Overrides
Ha egy Scene béli prefab példányon változtatásokat hajtunk végre, akkor azok a változtatások nem fognak megjelenni automatikusan minden egyéb példányán is a Prefab-nak.
Ennek az az oka, hogy ezek a változtatások nem lesznek egyből a prefab részei, hanem csak az adott prefab példányhoz társított módosítások maradnak, amik a Scene-en belül tárolódnak el.
Nézzük a korábbi példát az ellenfelekkel. Az a bizonyos gyakori ellenfél legyen például egy kobold. Ha a kobold Prefab-on módosítjuk annak egy beállítását, teszem azt az ugrása magasságát állítjuk nagyobbra, akkor minden egyes kobold a játékban nagyobbat fog tudni ugrani.
Ezzel szemben, ha egyetlen kobold példány beállitását módosítjuk, akkor csak az az egy fog változni.
Egy prefab példányt tehát az eredeti prefab file és a Scene-ben lévő egyedi módosítások írnak le.
Ezeket az egyedi felülírásokat override-nak nevezzük.
Azt, hogy egy prefab példány melyik beállítása jön az eredeti Prefab fájlból és melyik egyedi Scene-ben tárolt felülírás, azt a következő módokon tudjuk megnézni:
- Inspector ablak
A prefab példányon a módosított beállítások vastaggal szedve jelennek meg az Inspector ablakban.
Emellett ezen beállítások neve mellett bal oldalt egy kis kék függőleges vonal jelenik meg.
Ugyanez a kis kék vonalka jelenik meg a Hierarchy ablakban is azon Prefab-ok esetében, amik bármi egyedi módosítást tartalmaznak.
A (bal) fenti példában a Car prefab példány tartalmaz egyedi módosításokat de a Car2 nem.
A Car példányon (fent jobbra) felül van írva a Rigidbody komponensen a Mass és a Constrains, a BoxCollider-en meg a Size beállítás, annak is csak a Z értéke.
- Override menü
A prefab példányoknak megjelenik egy extra sor a GameObject Insperctor fejlécében a Tag és a Layer alatt. Az Open gomb megnyitja a Prefab módú szerkesztést (később), a Select gomb kijelöli a példányhoz tartozó Prefab filet, az Override gomb alatt pedig ha megnyomtuk, kinyílik egy menü, amiben fel van sorolva az összes olyan komponens amin változtatások történtek. Egy komponenst kiválasztva megtekinthető, az összes különbség az eredeti prefab fájl és a egyedi változtatott verziók közt.
Egy Prefab példányon-on módosítható minden beállítás egyedileg, beleértve a SerializeField-eket a saját MonoBehaviour szkripteken. Teljesen új komponenst is adhatunk egy GameObject-hez vagy törölhetjük is őket egy Prefab példányon belül úgy hogy a módosítás csak Override marad. Ki és bekapcsolhatunk gyerek GameObjcteket, sőt hozzá is adhatunk teljesen újakat, egyedül törölni nem tudunk GameObject-et, mint egyedi Prefab módosítás.
Apply és Revert
Egyedi módosításokon két fontos műveletet tudunk elvégezni:
- Revert: A módosítás visszavonása az adott prefab példányra
- Apply: A módostás alkalmazása a Prefab file-ra
Az eygedi módosítás (override) megszűnik és a példány ezután a prefab fájlban szereplő beállítást fogja használni.
A módosítás ettől fogva nem a példányra vett egyedi override a Scene-ben, hanem a prefab file része és igaz lesz az összes többi prefabra is.
Mindkét műveletet elérjük egyszerűen a változtatott beállítás jobb egérgombbal előhívható menüjében.
A változtatások csoportosan is alkalmazhatók vagy visszavonhatók az Overrides menüben a Revert All, Apply All gombokkal.
Egy módosítást csak akkor nem tudunk Apply-olni, ha az egy referencia egy GameObject-re vagy komponesre a Scene-en belül. Ez azért van így mert Prefab file nem tartalmazhat referenciát egy Unity Object-re saját magán kívül.
A prefab mód
Az egyik módja a Prefab-okon való munkának, hogy egy Prefab-on egyedileg módosítjuk a beállításokat és utána azt alkalmazzuk a Prefab fájlra, a módosítás ezáltal megtörénik minden példányon.
Másfelől ha biztosan nem egyedi változást akarunk végrehajtani, akkor nem kell példányon dolgoznunk, hanem egyből módosíthatjuk a Prefab fájlt magát. Ehhez be kell lépnünk prefab módba. Ez alapvetően kétféleképp érhető el.
- A prefab fájlon keresztül
- Duplán a fájlra rákattintva.
- Egy prefab példányon keresztül
- A hierarch ablakban a példány melletti jobbra mutató apró nyílra kattintva.
- A prefab fájl Inspector ablakának fejlécében a Select gombra kattintva.
A második mód kicsivel eltér az elsőtől. Utóbbi esetben egy konkrét Prefab példányon dolgozunk. Ekkor az adott példány látszik a Scene ablakban annak környezetében, minden egyéb objektum azonban csak kifakulva látszik körülötte.
Mindkét esetben a prefab módban történt módosítások a Prefab fájlba mentődnek, tehát egyből alkalmazva lesznek az összes példányra.
Nested Prefab
Képzeljünk el egy újabb példát! Ezúttal is azokon a koboldokon dolgozunk, amik gyakran vannak jelen a játékban. Ezen koboldoknak van egy fegyverük is, ami szintén egy bonyolult objektum sok beállítással. A fegyver, egy nyílpuska és sok más helyen is feltűnik a játékban. A játékos karakter és más ellenfelek kezében is lehet ugyanilyen nyílpuska. Fontos, hogy minden esetben a nyílpuska ugyanúgy működjön bárki kezében is van. Ezáltal kis gyakorlás után a játékos ránézésre fogja tudni annak tulajdonságait, bárhol is találkozik vele a játékban.
Ebben az esetben a nyílpuskát is érdemes egy Prefab-ként megalkotni és azt a kobold Prefab részévé tenni. Ezt nevezzük nested azaz beágyazott Prefab-nak: Mikor az egyik Prefab része egy referencia a másik Prefab-ra.
Ahogy a Scene sem tartalmazza a Prefab-hoz tartozó adatokat, mikor annak egy példánya van jelen a Scene-ben, úgy a Prefab sem tartalmazza az adatokat a belé ágyazott nested Prefab-ból, csak egy referenciát az adott (nested Prefab) fájl-ra.
Ebben az esetben, ha a nyílpuska Prefab-on változtatunk valamit, az meg fog történni minden egyes kobold példányon, de minden egyéb helyen is, ahol a fegyver jelen va.
E módon bárhány szintbe rendezhetők a nested Prefab-ok (Prefab-ba ágyazott Prefab-ba ágyazott Prefab…). A gyakorlatban ritka, hogy ez 2-3 szint fölé nőjön.
Tegyük fel, hogy egy olyan Prefab példánya van jelen egy Scene-ben, aminek magának is van beágyazott Prefab-ja, és egy egyedi módosítást hajtunk végre ezen a Scene beli példányon, annak is a beágyazott Prefab részén.
Ha az előző példát visszük tovább, akkor teszem azt a nyílpuska lövési sebességét állítjuk nagyobbra az egyik Scene-ben lévő kobold példányon. Ebben az esetben a változtatás a megszokott módon egyedi lesz arra az egy ellenfél példányra. Sema többi hasonló fegyver, sem a többi hasonló kobold nem fog más sebességgel lőni, csak az az egy. A változtatás a Scene részét képező egyedi override lesz csak.
A változást a hagyományos módon vissza lehet vonni a Revert paranccsal, de ha alkalmazni vagy más néven Apply-olni szeretnénk, akkor már két lehetőségünk van:
- Alkalmazhatjuk a változást a külső Prefab-ra (példában kobold Prefab-ra).
- Alkalmazhatjuk a változást a nested Prefab-ra (példában nyílpuska Prefab-ra).
Az első esetben a változás továbbra is egy override lesz, de nem a Scene-ben, hanem a külső Prefab-ban. Ekkor minden kobold gyorsabban fog lőni, de ha a játtékban máshol fordul elő a nyílpuska, az nem kapja meg ezt az egyedi változást.
A második esetben ha a változást egészen a nested Prefab-ra hajtjuk végre, akkor már minden játék beli nyílpuska máshogy fog működni, nem csak a koboldoké.
Prefab Variant
A koboldokról tudnivaló, hogy a zöldek nyílpuskát használnak, a pirosak kardot, a szürkék, pedig buzogányt. Habár vannak tulajdonságok, amikben eltérnek, a különböző színű koboldok a legtöbb szempontból hasonlóak. Ugyanakkorát ugranak, ugyanannyi életük van, ugyanolyan gyorsan futnak, ugyanaz a Mesh írja le az alakjukat és az animációjuk nagy része is megegyezik. Egyedül a fegyverük és a színük (más material) különbözik.
Ebben az esetben az új piros és szürke koboldjaik nem teljesen új entitások, csak változatai vagy más szóval variánsai az eredeti kobold prefab-nak.
A prefab variáns egy olyan előre elkészített, újrahasznosítható változata egy másik Unity prefab-nak, amely többnyire ugyanazokat az alapokat használja mint az eredeti, de bizonyos tulajdonságai felülírtak.
A prefab variánsok lehetővé teszik a játékfejlesztők számára, hogy különböző változatokat hozzanak létre egy prefab-ból amellett, hogy megőrzik az eredeti GameObject tulajdonságainak és belső szerkezetének nagy részét.
Egy prefab fájlból variánst úgy hozhatunk létre, hogy a Project ablakon jobb egérgombbal a filera kattintva előhívjuk annak menüjét és ott a “Create / Prefab Variant“ lehetőséget választjuk.
Ebben az esetben is a variánson végzett egyedi változtatásokat, más néven override-okat vissza tudjuk vonni a Revert paranccsal, valamint alkalmazni is az eredeti prefabra az Apply paranccsal.
Bármely szerializált információ felülírható egy variánson.
Prefab variánsok használata javíthatja a fejlesztés folyamatának hatékonyságát, és lehetővé teszi, hogy a játékfejlesztők gyorsabban testre szabhassák a játékot a felhasználók igényeinek megfelelően.
Unpack prefab
Ha egy Scene béli prefab példányból egy egyszerű, mezei GameObject-et szeretnénk készíteni, ami ugyen másolata az eredeti Prefab-nak, de többé nincs kapcsolata a Prefab fájjllal, akkor azt az “Unpack Prefab” paranccsal tudjuk megteni.
Ezt a lehetőségeet Hierarchy ablakban a Prefab példányra kattintva jobb egérgombbal érjük el: Jobb kattintás a prefab példányon / Prefab / Unpack Prefab.
Ha itt az “Unpack Prefab Completely” lehetőséget választjuk akkor a beágyazott Prefab-ok referenciái is megszünnke.
Ezután semmi módositás a Prefab-on nem fog megtörténi ezen a GameObject-en, és forditva is semmilyen változtatás ezen a GameObject-en nem lesz alkalmazhetó az eredeti Prefab filera.
Architektúra prefabokkal
Ahogy a programozásban kiemelten fontos a kódduplikálás elkerülése, úgy projekt tervezésnél ugyanakkora figyelmet kell fordítania arra, hogy az adatokat se duplikáljuk.
A Prefab-ok a legelemibb eszközeink közé tartoznak, ahhoz, hogy elkerüljük a redundanciát (vagyis az adatok többszörös tárolását) egy Unity projektben.
Ahogys redundáns kód úgy redundáns adat is hibás működéshez vezethet és ami még rosszabb, a hibát felismerni és megtalálni a gyakorlatban különösen nehéz tud lenni. Inkább javasolet minderre a tervezési fázisbanfigyelni.