08. Ovládanie game objektov
V minulej lekcii sme pristupovali z jedného komponentu na game objekte do iného komponentu na tom istom game objekte. Teraz sa naučíme pristupovať aj na iné game objekty.
Budeme pokračovať v projekte z minulej lekcie. Vyrobíme si dve ďalšie otáčajúce sa kocky a dorobíme skript na prepínanie medzi nimi.
- Vytvorte dve ďalšie kocky (napríklad zduplikovaním existujúcej kocky)
- Uistite sa, že sú aj na nových kockách pridané komponenty Rotator a Ovladanie
- Zmeňte farby na kockách tak, aby jedna bola červená, jedna zelená a jedna modrá
- Premenujte kocky na Kocka1, Kocka2, Kocka3
- Zoraďte kocky vedľa seba ako na obrázku:

Vytvorte skript Prepinanie a priraďte ho na game objekt Main Camera
Úlohou skriptu Prepinanie bude ovládať otáčanie na jednotlivých kockách. Urobíme to tak, aby po stlačení príslušnej klávesy ostala rotovať len vybraná kocka.
Ovládanie cudzieho game objektu
Skript Prepinanie nevie nič o game objektoch Kocka1, Kocka2, Kocka3. Vie maximálne tak o „svojom“ game objekte Main Camera, na ktorom je priradený. Musíme ho teda nejak napojiť na naše kocky.
Napojenie vytvoríme cez vlastnosti skriptu. Vytvoríme mu tri nové vlastnosti a do nich ako hodnoty priradíme naše kocky.
Akého typu budu tieto nové vlastnosti? Nebudeme do nich priraďovať čísla, takže nebudú to int
ani float
typy. Nebudeme do nich priraďovať ani true/false logické hodnoty, takže nebude to ani typ bool
.
Budeme do nich priraďovať game objekty a na to slúži typ GameObject
. Tieto nové vlastnosti pomenujeme cubeA
, cubeB
a cubeC
:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Prepinanie : MonoBehaviour { public GameObject cubeA; public GameObject cubeB; public GameObject cubeC; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } }
Pozrime sa do inšpektora na game objekte Main Camera ako tam vyzerá komponent Prepinanie:

Pribudli tam tri nové vlastnosti Cube A, Cube B a Cube C. Inšpektor hlási, že zatiaľ v nich nič nie je uložené : None.
Priraďme do vlastnosti Cube A kocku Kocka1. Urobíme to tak, že jednoducho pretiahneme z hierarchie game objekt Kocka1 sem do inšpektora do vlastnosti Cube A. Do vlastnosti Cube B priraďte Kocku2 a do Cube C priraďte Kocku3.
Teraz už skript vie o každej našej kocke. Poďme urobiť, aby sa pri stlačení klávesy A zaplo točenie na prvej kocke a aby sa vyplo točenie na druhej a tretej kocke.
void Update() { if (Input.GetKeyDown(KeyCode.A)) { // Tu budu prikazy na zapnutie a vypnutie tocenia } }
Keď sme predtým v minulej lekcii vypínali komponent Rotator, robili sme to na „svojom“ game objekte a vyzeralo to takto:
GetComponent<Rotator>().enabled = false;
Ak budeme teraz chcieť zapnúť Rotator na inom game objekte – napríklad na takom, ktorý je uložený vo vlastnosti cubeA
, bude to vyzerať podobne:
void Update() { if (Input.GetKeyDown(KeyCode.A)) { cubeA.GetComponent<Rotator>().enabled = true; } }
Pridajte do tela tejto podmienky príkazy na vypnutie točenia na game objektoch cubeB
a cubeC
.
Dorobte do metódy Update
podobným spôsobom aj stlačenie klávesy B (zapne točenie na druhej kocke, vypne točenie na prvej a tretej) a stlačenie klávesy C (zapne točenie na tretej kocke, vypne točenie na prvej a druhej).
Takýmto spôsobom vieme napojiť hocijaký náš skript na hocijaký iný game objekt.
Game objekty ako premenné
S premennými (a teda aj s vlastnosťami) typu GameObject
sa dá narábať ako s inými premennými. Nevieme s nimi síce robiť aritmetické operácie, ale vieme im priraďovať hodnoty z iných premenných typu GameObject
.
Teraz si vytvoríme v skripte Prepinanie jednu vlastnosť, do ktorej si budeme ukladať tú kocku, ktorá sa práve otáča. Bude to naša „aktívna kocka“.
Vytvoríme si teda v skripte novú vlastnosť typu GameObject
a nazveme ju activeCube
:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Prepinanie : MonoBehaviour { public GameObject cubeA; public GameObject cubeB; public GameObject cubeC; GameObject activeCube; // Start is called before the first frame update void Start() { }
Prečo sme pred túto vlastnosť nedali public
? Lebo je vlastnosť, ktorú nepotrebujeme meniť v inšpektore, ani ju nepotrebujeme sprístupniť iným komponentom. V takom prípade nemusí byť vlastnosť „zverejnená“ (public
). Takáto vlastnosť sa vola aj privátna a vie s ňou pracovať len ten skript, ktorému patrí.
A pri stlačení klávesy A,B alebo C priradíme do tejto vlastnosti príslušnú kocku:
void Update() { if (Input.GetKeyDown(KeyCode.A)) { cubeA.GetComponent<Rotator>().enabled = true; cubeB.GetComponent<Rotator>().enabled = false; cubeC.GetComponent<Rotator>().enabled = false; activeCube = cubeA; } if (Input.GetKeyDown(KeyCode.B)) { cubeA.GetComponent<Rotator>().enabled = false; cubeB.GetComponent<Rotator>().enabled = true; cubeC.GetComponent<Rotator>().enabled = false; activeCube = cubeB; } if (Input.GetKeyDown(KeyCode.C)) { cubeA.GetComponent<Rotator>().enabled = false; cubeB.GetComponent<Rotator>().enabled = false; cubeC.GetComponent<Rotator>().enabled = true; activeCube = cubeC; } }
A môžeme si rovno aj overiť, aký game objekt je uložený v aktívnej kocke. Necháme si do konzoly vypisovať meno tohto game objektu:
void Update() { if (Input.GetKeyDown(KeyCode.A)) { cubeA.GetComponent<Rotator>().enabled = true; cubeB.GetComponent<Rotator>().enabled = false; cubeC.GetComponent<Rotator>().enabled = false; activeCube = cubeA; } if (Input.GetKeyDown(KeyCode.B)) { cubeA.GetComponent<Rotator>().enabled = false; cubeB.GetComponent<Rotator>().enabled = true; cubeC.GetComponent<Rotator>().enabled = false; activeCube = cubeB; } if (Input.GetKeyDown(KeyCode.C)) { cubeA.GetComponent<Rotator>().enabled = false; cubeB.GetComponent<Rotator>().enabled = false; cubeC.GetComponent<Rotator>().enabled = true; activeCube = cubeC; } Debug.Log(activeCube.name); } }
Skúste skript uložiť a spustiť hru. A poďme sa pozrieť do konzoly:

Je to doslova červené more chybových hlášok. Kde je problém?
Môj prvý null
Žiaden strach. Toto je pri programovaní jedna z najčastejších chýb a urobíte ju ešte veľakrát. Preto je dôležité vedieť ju dešifrovať a nájsť, kde sa skrýva. Chybová hláška nám hovorí „Object reference not set to an instance of object“, preložené do slovenčiny to znamená „Odkaz na objekt nebol nastavený na nejaký objekt“.
V ľudskej reči to znamená, že naša vlastnosť activeCube
, ktorá má ukazovať na niektorú práve aktívnu kocku, ešte nebola nastavená na žiadnu z našich kocku. Pretože sme ešte nestlačili žiaden kláves. A preto, keď chceme vypísať meno aktívnej kocky, počítač nenájde na v premennej activeCube
žiadnu kocku a vyhodí chybu. Vlastnosť activeCube
je na začiatku len prázdna nádobka v špajzi počítačovej pamäte a nachádza sa v nej jedno veľké nič.
Technicky by sme to vedeli vyriešiť tým, že do activeCube
priradíme na začiatku chodu skriptu nejakú z kociek cubeA
, cubeB
, cubeC
. Ale my dopredu nevieme, či používateľ stlačí najprv A, B alebo C.
Vieme však položiť počítaču podmienku, nech vypisuje meno aktívnej kocky do konzoly iba ak vlastnosť activeCube
nie je prázdna.
Prázdna hodnota je zvláštny koncept pri programovaní. Keď vytvoríme číselnú premennú typu int
a nepriradíme do nej žiadnu hodnotu, počítač do nej dá default hodnotu 0
. Pri premennej typu float
to bude hodnota 0.0f
. Ale pri premennej (alebo vlastnosti) typu GameObject
nepoznáme žiaden nulový game objekt. Tak sa používa namiesto neho špeciálna hodnota zvaná null
.
Ak chceme teda napísať našu podmienku v tvare „Ak nie je activeCube prázdna, vtedy vypíš meno objektu uloženého v activeCube“, bude to vyzerať takto:
void Update() { if (Input.GetKeyDown(KeyCode.A)) { cubeA.GetComponent<Rotator>().enabled = true; cubeB.GetComponent<Rotator>().enabled = false; cubeC.GetComponent<Rotator>().enabled = false; activeCube = cubeA; } if (Input.GetKeyDown(KeyCode.B)) { cubeA.GetComponent<Rotator>().enabled = false; cubeB.GetComponent<Rotator>().enabled = true; cubeC.GetComponent<Rotator>().enabled = false; activeCube = cubeB; } if (Input.GetKeyDown(KeyCode.C)) { cubeA.GetComponent<Rotator>().enabled = false; cubeB.GetComponent<Rotator>().enabled = false; cubeC.GetComponent<Rotator>().enabled = true; activeCube = cubeC; } if (activeCube != null) { Debug.Log(activeCube.name); } }
Pre osvieženie pamäti pripomínam, že !=
znamená „nerovná sa“ alebo aj „je rôzne od…“. Takže v predpoklade activeCube != null
sa pýtame, či hodnota activeCube
je rôzna od prázdnej hodnoty. Inými slovami – pýtame sa, či v activeCube
už niečo je. Niečo, hocičo. Akonáhle tam počítač nájde niečo uložené, predpoklad je splnený a podmienka sa vykoná.
Skúste spustiť takto upravený skript. Na začiatku nebude konzola vypisovať nič, žiadnu chybovú hlášku. Ale ani meno aktívnej kocky.
A keď stlačíte v hre A, B alebo C, tak sa začne do konzoly vypisovať meno aktívnej kocky:

Tieto výpisy sa tam budú teraz vypisovať donekonečna, lebo sme príkazy vložili do metódy Update
, ktorá sa vykonáva neustále. Z toho istého dôvodu sme mali pred chvíľou konzolu plnú stoviek červených chybových hlášok, aj keď chyba bola v skutočnosti iba jedna. Bola iba jedna, ale opakovala sa neustále.
Zhrnutie
Pristupovať z jedného game objektu na iný vieme v skripte tak, že vytvoríme pomocou vlastnosti prepojenie z nášho skriptu na cudzí game objekt. Táto vlastnosť potom predstavuje cudzí game objekt.
Od takto napojeného game objektu si vieme cez príkaz GetComponent<>()
vypýtať aj jeho komponenty a pracovať s nimi v skripte – meniť im vlastnosti alebo ich vypínať ich.
Ak v takejto vlastnosti typu GameObject
nie je ešte nič uložené, je v nej uložená prázdna hodnota null
. S takouto prázdnou hodnotou sa nedá pracovať a generuje to chyby.
Hodnoty premenných a vlastností typu GameObject
vieme medzi sebou priraďovať.
Bonus
Ak vás hnevá, že máte teraz konzolu zapchatú nekončiacimi výpismi mien aktívnych kociek, tak tu je jednoduchá modifikácia podmienky, ktorá zabezpečí, že sa meno kocky nebude vypisovať neustále, ale len keď stlačíme nejakú klávesu:
if (activeCube != null && Input.anyKeyDown) { Debug.Log(activeCube.name); }
Input.anyKeyDown
je hodnota, ktorá jetrue
ak len bola stlačená hocijaká klávesa. Inak je tofalse
.&&
je logická spojka s významom „a zároveň“
V takto upravenej podmienke sa teda pýtame: Ak activeCube
nie je prázdna a zároveň bola stlačená nejaká klávesa, tak potom vypíš meno aktívnej kocky. Ale o logických spojkách niekedy inokedy…
Hotový projekt je tu: Programovanie_08.zip