05. Logika hry
Keď už máme potrebné ovládanie, je čas zapracovať do hry samotnú logiku. Dnes tu nebude nič prevratne nové, všetko sme už niekde skôr použili. Ale tréning robí majstra.
Otvorte si svoj projekt alebo si stiahnite hotový: SpaceGame_04.zip
Sandra má za úlohu prežiť, kým je príde niekto zachrániť. Za ten čas jej dochádza kyslík a musí si ho dopĺňať z voľne pohodených nádrží. Aby hra mala nejaké napätie, tak získavanie kyslíka musí niečo stáť, napríklad obmedzené palivo v tryskách alebo zdravie po zrážke s UFOm. Ale všetko postupne. Začnime s časom a kyslíkom.
Kostra hernej logiky
Aké údaje si k tomu budeme musieť uchovávať?
- Koľko času ostáva do príchodu záchrany
- Koľko kyslíka ešte ostáva Sandre
A ako bude fungovať základná logika hry?
- Ak čas zostávajúci do príchodu klesne na nula – Sandra vyhrala
- Ak kyslík klesne na nula – Sandra prehrala
- Čas klesá neustále automaticky
- Kyslík klesá neustále automaticky
- Kyslík stúpa po zachytení kyslíkovej fľaše
Pre hernú logiku si vytvorte nový game objekt Hra a skript Main.cs, ktorý na priraďte na game objekt Hra.
Odpočítavanie času
Aby sme si mohli uchovávať informáciu o tom, koľko času a koľko kyslíka ostáva, vyrobme si na to vlastnosti v triede Main:
public class Main : MonoBehaviour
{
// Kolko casu ostava do zachrany (v sekundach)
public float time = 60;
// Kolko kyslika ostava (v percentach)
public float oxygen = 100;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Čas má klesať neustále, takže kam dáme príkaz na zmenšovanie času? Do Update(). A o koľko znížime čas v jednom snímku? O toľko, koľko času v danom snímku uplynulo (Time.deltaTime). A hneď aj skontrolujme, či už čas neklesol na nulu:
void Update()
{
// Zniz cas o deltaTime
time -= Time.deltaTime;
// Ak uz cas klesol na nulu alebo pod nulu
if (time <= 0)
{
// Tak je hra vyhrata
// TODO
}
}
Možno sa pýtate, prečo kontrolujeme, či je čas menší alebo rovný nule (time <= 0) a nie iba, či je rovný nule (t == 0). Je to podobné ako s UFO, či dorazilo do cieľa. Keď sa čas znižuje po rozmanitých dielikoch, tak nemusí klesnúť presne na nulu, ale napríklad na -0.02. Keby sme sa v takom prípade pýtali, či je čas rovný nule, tak počítač odpovie: „Nie, čas nie je rovný nule“. A pokračuje so znižovaním času do mínus nekonečna.
Keď som bol malý, mama ma raz nechala samého doma s inštrukciami: „V rúre sa pečie koláč, keď bude červený, tak rúru vypni a koláč vyber“. Tak som sedel pred rúrou a pozeral na koláč. Najprv bol biely, potom bol žltý, potom oranžový, potom hnedý, potom tmavohnedý a potom už bolo neskoro. Tažký je život programátora.
TODO je univerzálna prokrastinačná formulka, že toto dorobíme neskôr
A aby sme videli, koľko času ostáva, vyrobme si do scény nejaký text, ktorý to bude ukazovať.
- Vytvorte v scéne UI Text a pomenujte ho Casomiera.
- Vyplňte ho nejakou povzbudzujúcou hláškou:

Teraz prepojíme tento UI Text s logikou v hre, aby ukazoval aktuálny čas. Akurát herná logika v triede Main zatiaľ nevie o tom, že vôbec existuje nejaký Casomiera. Preto jej vytvoríme novú vlastnosť typu Text a Casomieru do nej priradíme:
public class Main : MonoBehaviour
{
// Kolko casu ostava do zachrany (v sekundach)
public float time = 60;
// Kolko kyslika ostava (v percentach)
public float oxygen = 100;
// Text, ktory zobrazuje cas
public Text timeDisplay;

Casomiera je už teda je napojená na Main, ešte aby jej Main aj povedal, aký čas má ukazovať. Chceme, aby sa to zmenilo vždy, keď sa čas zmení, takže v každom Update():
void Update()
{
// Zniz cas o deltaTime
time -= Time.deltaTime;
// Ak uz cas klesol na nulu alebo pod nulu
if (time <= 0)
{
// Tak je hra vyhrata
// TODO
}
// Aktualizuj cas na obrazovke
timeDisplay.text = "Time to rescue: " + time.ToString("00.00");
}
Uložiť, spustiť, potešiť sa, ako to pekne funguje:

Odpočítavanie kyslíka
Kyslík sa bude takisto ako čas odpočítavať neustále – takže v Update(). Ale je tam jeden rozdiel. Čas za 1 sekundu klesne o 1 sekundu, to dá rozum. Ale zásobu kyslíku počítame v percentách. O koľko percent klesne kyslík za 1 sekundu? Zatiaľ sa pre jednoduchosť dohodnime, že kyslík bude klesať 3x rýchlejšie ako čas. Nech má Sandra motiváciu naháňať sa za fľašami.
void Update()
{
// Zniz cas o deltaTime
time -= Time.deltaTime;
// Zniz kyslik o 3-nasobok deltaTime
oxygen -= 3 * Time.deltaTime;
// Ak uz cas klesol na nulu alebo pod nulu
if (time <= 0)
{
// Tak je hra vyhrata
// TODO
}
// Aktualizuj cas na obrazovke
timeDisplay.text = "Time to rescue: " + time.ToString("00.00");
}
- Vytvorte UI Text s nazvom Kyslikomiera a napojte ho na Main podobne ako Casomieru.
- Pridajte do kódu triedy
Mainpríkaz, ktorý bude aktualizovať text v Kyslikomiere.

A ešte nám chýba overenie, či už kyslík neklesol na nulu:
void Update()
{
// Zniz cas o deltaTime
time -= Time.deltaTime;
// Zniz kyslik o 3-nasobok deltaTime
oxygen -= 3 * Time.deltaTime;
// Ak uz cas klesol na nulu alebo pod nulu
if (time <= 0)
{
// Tak je hra vyhrata
// TODO
}
// Ak uz kyslik klesol na nulu alebo pod nulu
if (oxygen <= 0)
{
// Tak je hra prehrata
// TODO
}
// Aktualizuj cas na obrazovke
timeDisplay.text = "Time to rescue: " + time.ToString("00.00");
// Aktualizuj kyslik na obrazovke
oxygenDisplay.text = "Oxygen: " + oxygen.ToString("00") + "%";
}
Pripočítavanie kyslíka
Kedy chceme pripočítavať kyslík? Keď Sandra narazí do poletujúcej fľaše. My sme si už minule predpripravili kus kódu, kde konzumujeme fľašu. Je to v skripte Astronaut:
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Kolizia!");
// Ak sme sa zrazili s konzumovatelnym kyslikom
if (collision.gameObject.tag == "Consumable")
{
// Tak skonzumuj kyslik
// (toto zatial nemame)
// A zlikviduj kyslikovy objekt
Destroy(collision.gameObject);
}
}
Skonzumovať kyslík tu znamená, doplniť si ho z fľaše do skafandra. Objem kyslíku si evidujeme v skripte Main a skript Astronaut o ňom ale nevie, lebo každý je na inom game objekte (Main je na Hra a Astronaut je na Sandra). Tak napojíme Main na Astronauta, takisto ako sme napájali Casomieru a Kyslikomieru na Main.
Vytvorte v triede Astronaut novú vlastnosť, bude verejná (aby sme ju mohli v editore nastaviť), bude typu Main (lebo bude odkazovať na skript Main) a bude sa volať main.
Takéto prepájanie game objektov medzi sebou je podstatná časť práce a logiky v Unity, preto je dobré si to osvojiť ako rutinu.
public class Astronaut : MonoBehaviour
{
public float speed = 1;
public float rotationSpeed = 180;
// Odkaz na hlavny skript, ktory budeme informovat o prijati kysliku
public Main main;

Keď je takto Sandra prepojená na Hru, tak komponent Astronaut na Sandre vie pristupovať k verejným vlastnostiam komponentu Main na Hre, napríklad k stavu kyslíku vo vlastnosti oxygen. A vie ju zvýšiť o kyslík nabratý z fľaše, povedzme o 2 percentá:
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Kolizia!");
// Ak sme sa zrazili s konzumovatelnym kyslikom
if (collision.gameObject.tag == "Consumable")
{
// Tak pridaj kyslik
main.oxygen += 2;
// A zlikviduj kyslikovy objekt
Destroy(collision.gameObject);
}
}
A to je základná kostra hry.
Máme motiváciu: Prežiť.
Máme niečo, čo nás ohrozuje: Klesajúci čas.
Máme niečo, čo nám pomáha: Kyslíkové fľaše.
A máme prekážku, ktorú musíme prekonávať: Navigáciu lietaním v priestore a otravné UFO.
Ďalšie kroky v budúcnosti by boli napríklad vyladenie konštánt v hre: koľko kyslíku ubúda, koľko pribúda, koľko času na začiatku hry ostáva do záchrany… Pre spestrenie hry je možno pridať nejaké zhoršenie stavu kyslíka pri zrážke s UFO, či zvýšená spotreba kyslíka pri otáčaní sa Sandry. Dajú sa tiež pridať nejaké ďalšie powerupy s tagom „Consumable“ – väčšie fľaše, ktoré obsahujú viac kyslíka, palivo, ktoré zvyšuje Sandre rýchlosť pohybu, vysielačka, ktorá skráti čas do záchrany…
Možností je vždy veľa a nakombinovať ich tak, aby bola hra je chytľavá a dobre hrateľná, nie je ľahká vec.
Zhrnutie
Rôzne game objekty, respektíve skripty na nich, musia v aplikácii navzájom vedieť komunikovať. Hlavný skript musel textovému game objektu nastaviť text. Skript na astronautke musel hre nahlásiť, že pribudol kyslík do skafandra. K tomu slúži prepájanie game objektov, aké sme v tejto lekcii precvičili.
Pri uvažovaní o tom, ako napísať skript, nám pomáha premyslieť si, aké údaje si potrebuje skript evidovať, s ktorými inými game objektami potrebujeme skript komunikovať.
Tiež pomôže uvedomiť si, ktoré veci sa majú kedy odohrať:
- Ak sa to má odohrať raz na začiatku (napr. vygenerovanie fliaš), ide to do metódy
Start() - Ak sa to deje permanentne (napríklad ubúdanie kyslíka), ide to do metódy
Update() - Ak sa to deje, len následkom nejakej udalosti (napríklad pribudnutie kyslíka pri zachytení fľaše), ide to do niektorej z metód súvisiacich s udalosťami (napríklad
OnCollisionEnter2D())
Na domácu úlohu
Doplňte do hry, čo sa má stať, keď sa minie kyslík alebo keď sa minie čas. To sú tie dve TODO, ktoré sme si nechali v skripte Main. V prvom prípade sa môže zobraziť nejaká hláška o úspešnej záchrane, v druhom nejaká o smutnom konci. V oboch prípadoch budete chcieť asi aj zastaviť čas, aby sa už hra nehýbala a aby Kyslikomiera alebo Casomiera neklesali do záporných čísel.
Ošetrite pri získavaní kyslíka to, aby kyslík nevystúpal nad 100. Predsa len, mať 105% niečoho… to už vyzerá ako volebná účasť v Bielorusku. Ako to ošetriť? Po pripočítaní kyslíka sa spýtať, či je jeho hodnota viac ako 100. A ak je, tak ju nastaviť na rovných 100.

Tu je na stiahnutie hotový projekt: SpaceGame_05.zip
