kpi

Magazín KPI

№ 6 / máj 2019

Vytvorenie hry pomocou Unity3D

Unity3D je jedným z najpopulárnejších nástrojov pre vývoj hier pre rôzne platformy od počítačov a konzol, cez mobilné zariadenia, až po web. V tomto článku v krátkosti predstavíme použitie tohto nástroja a ukážeme jednotlivé kroky vývoja jednoduchej hry CatchTheBus.

Úvod do Unity

Unity3D je voľne dostupný nástroj na vytváranie hier s vlastným zabudovaným vývojovým prostredím. Vďaka svojmu grafickému rozhraniu a nenáročnosti je tento nástroj v okruhu vývojárov mimoriadne populárny. Pomocou Unity si vie každý záujemca jednoducho osvojiť základy vyvíjania hier. Ako nám aj názvom prezradzuje, popri tvorbe hier v 2D ponúka aj možnosť modelovania 3D, dokonca je vhodný aj na prípravu krátkych filmov. Ďalšou výhodou je, že umožňuje vytvárať multiplatformné aplikácie, preto ho môžu používať používatelia systémov Windows, Android, Xbox, PS4 ale aj ​​sociálnej siete Facebook. Unity poskytuje vlastný obchodom herných kompoenntov (Asset Store), ktorý vieme používať priamo cez editor. Tu si vieme zabezpečiť užitočné doplnky pre našu hru, napr. grafiku, animácie, textúry a pod. Vďaka prvkom používateľského rozhrania (UI) vieme vytvárať naozaj vkusný dizajn, pričom nám kódy vytvorené jazykom C# zabezpečujú funkcionalitu našej hry alebo aplikácie. Unity ponúka aj rôzne ďalšie služby, ako napr. Unity Cloud Build, ktorá nám projekt automaticky preloží a otestuje. Do hry vieme zabudovať aj reklamy alebo možnosť nákupu, dokonca vieme vytvárať aj ​​hry pre viacerých hráčov. Na domovskej stránke nástroja nájdeme videonávody, ktoré pomáhajú s prácou v Unity, ako aj fórum, ktorý ponúka možnosť kladenia otázok a diskusie s ostatnými užívateľmi programu.

Práca so scénami

Unity pracuje so scénami, ktoré obsahujú herný svet. V týchto scénach sa ukladajú aj rôzne úrovne hry. Každá scéna je označená indexom — číslom, ktoré začína od nuly. Pomocou týchto čísiel vieme so scénami manipulovať. Ak nie sú definované špeciálne podmienky, scény sa zobrazujú ​​vo vzostupnom poradí podľa ich indexu.

Hlavná kamera

Každá scéna obsahuje objekt Main Camera. ​​Tento objekt predstavuje kameru, ktorá zobrazuje náhľad herného sveta, do ktorého vkladáme ďalšie objekty. Objekty ktoré ​​sa nachádzajú mimo náhľadu kamery ostávajú počas hry neviditeľné. Každá scéna môže obsahovať okrem základného náhľadu kamery aj ďalšie, ak si to hra alebo aplikácia vyžaduje, napr. ide o rôzne miestnosti v hre.

Editor a jeho časti

Po spustení editora si môžeme vybrať z piatich možností pracovnej plochy. V tomto článku predstavujeme možnosť Layout 2 by 3.

Editor má päť okienok:

  • Scene — okienko v ľavom hornom rohu ukazuje herný svet, náhľad kamery a sem sa vkladajú aj naše vlastné objekty.
  • Game — ukazuje hru, ktorá práve beží.
  • Hierarchy — okienko v strede ukazuje aktuálnu scénu, na ktorej práve pracujeme spolu s objektmi.
  • Project — obsahuje balíky (packages) a súčasti hry (assets), ktoré zahŕňajú skripty, obrázky (sprites), zvukové efekty, animácie a ďalšie.
  • Inspector — je veľmi dôležité okienko a je umiestnené vpravo: obsahuje komponenty objektov, vieme tu vykonať aj rôzne nastavenia.
Pracovná plocha Layout 2 by 3
Pracovná plocha Layout 2 by 3

Hra CatchTheBus

Po krátkom úvode postupujme k praktickej časti tvorby hry. Hra dostala názov CatchTheBus. Úlohou hlavnej postavy je stihnúť školský autobus, aby vedela včas odovzdať školské zadanie. K hre budeme potrebovať dve scény. Prvá, hlavná scéna sa objaví po spustení hry, kým druhá bude obsahovať samotnú hru.

K vytvoreniu hry budeme potrebovať nasledujúce objekty:

  • Player — hráč, ktorý sa vie na hernej ploche posúvať vodorovne a vyskočiť.
  • Bus — sa plynule posúva doprava.
  • Coffee — pomocou tohto objektu sa hlavná postava na pár sekúnd zrýchli.
  • Prekážky — ktoré spomaľujú hrdinu hry.
  • Objekty potrebné pre UI a zabezpečenie spätnej väzby — panely, textové objekty, tlačidlá, animácie.

Vytvorenie nového projektu

Pri vytváraní nového projektu sa môžeme rozhodnúť, či chceme hru 2D alebo 3D. Pri spustení editora bude náš adresár Asset obsahovať iba jediný podadresár Scenes so scénou nazývanou „SampleScene“. V prvom kroku zmeníme názov tejto scény na „MainScene“.

Vytváranie uvítacej obrazovky hry

V rámci scény nazývanej MainScene vytvoríme panel, ktorý bude zabezpečovať pozadie uvítacej obrazovky hry.

Vytvorenie nového panela v okienku Project
Vytvorenie nového panela v okienku Project

Rozmery panela v pixeloch nastavíme v okienku Inspector. Obrázok pozadia nastavíme tak, že súbor nahraný do Assetov jednoducho natiahneme (drag & drop) na vyhradené miesto v políčku Source Image.

Inspector okienko panela
Inspector okienko panela

Vytvoríme dve tlačidlá, ktoré v rámci kamerového náhľadu umiestnime do okienka Scene. Texty na tlačidlách sa dajú meniť v rámci textových objektov, ktoré sú súčasťami potomkov každého tlačidla. Predvolený vzhľad tlačidla si môžeme ľubovoľne prispôsobovať: môžeme doň umiestniť vlastný obrázok, nastaviť zmeny tlačidla v rôznych fázach kliknutia myšou (napríklad, tlačidlo zmení farbu pri kliknutí). Panel udalosti OnClick() sa nachádza na spodku komponentu Button (Script).

Inspector okienko tlačidla
Inspector okienko tlačidla

V OnClick() nastavíme pokyn, ktorý sa má vykonať po stlačení tlačidla. Budeme potrebovať jeden objekt a jeden skript. Vytvoríme jeden objekt s názvom SceneLoader v okienku Project a skript s názvom SceneLoaderScript v okienku Hierarchy. V skripte SceneLoaderScript vytvoríme novú funkciu Play(), ktorá zabezpečí prechod na ďalšiu scénu:

public void Play()
{
    SceneManager.LoadScene(1);
}

K objektu SceneLoader pridáme skript s názvom SceneLoaderScript ako komponent, potom posunieme objekt na miesto vyznačené None(Object). Takto sa sprístupní doteraz neprístupné tlačidlo No function, ktorému priradíme funkciu odštartovania hry, ktorá spustí hru prelistovaním na ďalšiu scénu. Celý proces je viditeľný na videoukážke. Druhé tlačidlo slúži na vypnutie hry. Do skriptu pridáme novú funkciu QuitGame(), ktorá bude vykonávať vypínanie. Objekt aplikujeme na OneClick() a zvolíme funkciu QuitGame(). Týmto krokom sme jednoducho dokončili uvítaciu obrazovku hry.

Uvítacia obrazovka hry
Uvítacia obrazovka hry

Vytváranie hlavnej postavy

Vytvoríme scénu, v ktorej bude samotná hra. Objekt obsahujúci hlavnú postavu sa dá vytvoriť dvoma spôsobmi.

  • Prvý spôsob — stačí, ak obrázok zobrazujúci hlavnú postavu natiahneme do kamerového náhľadu okienka Scene, týmto sa v okienku Hierarchy vytvorí nový objekt, ktorý zastupuje samotnú postavu hráča. Celý proces je viditeľný na videoukážke.
  • Druhý spôsob — vytvoríme nový herný objekt v okienku Hierarchy a do jeho časti Sprite aplikujeme obrázok obsahujúci danú postavu hráča.

Tento objekt následne premenujeme na Player a taktiež označíme pomocou značky (tag) ako „Player“.

Označenie herného objektu Player pomocou značky
Označenie herného objektu Player pomocou značky

K takto vytvorenému objektu pridáme komponent Rigidbody 2D, pomocou ktorého dostane objekt fyzické vlastnosti. Celý proces je viditeľný na videoukážke. Pôsobí naňho gravitácia, reaguje na kolízie s inými objektmi. Je dôležité dbať na to, ak sme k hre vyberali 2D komponenty, tak aj ostatné komponenty musia byť 2D. Ak nepoužívame 2D skripty, môže to spôsobiť problémy: napríklad objekt nebude reagovať na kolízie. Tieto vlastnosti sa dajú nastavovať v okienku Inspector. Ak by sme teraz spustili hru, postava hráča by v dôsledku gravitácie začala padať, vykročila by z kamerového náhľadu a padala by, kvôli predvolene nastavenej gravitácii. Potrebujeme podlahu, na ktorej by postava hráča mohla stáť. Podlahu vo forme obrázka natiahneme do okienka Scene, a tým získame ďalší objekt. Následne na postavu hráča nastavíme 2D circle collider a na podlahu 2D box collider. Ak teraz spustíme hru, postava hráča padne na podlahu.

Pridanie komponentu Circle Collider 2D
Pridanie komponentu Circle Collider 2D

Našou úlohou je zabezpečiť pohyb a skok postavy hráča. Vytvoríme skript s názvom PlayerController, ktorý bude komponentom objektu našej postavy hráča. Zavoláme statickú metódu, getAxis(). GetAxis() vráti hodnotu virtuálnej osi identifikovanej podľa axisName, a keďže sa náša postava pohybuje vodorovne, bude to hodnota Vertical. Skok vytvoríme tým istým spôsobom, rozdiel bude iba v axisName, Jump.

void FixedUpdate()
{ 
    moveX = Input.GetAxis("Horizontal") * Time.deltaTime * runSpeed;
    jumpY = Input.GetAxis("Jump") * Time.deltaTime * jumpSpeed;

    transform.Translate(moveX, jumpY, 0f);
}

V metóde getAxis je predvolene nastavený skok medzerníkom a pohyb doprava a doľava dvoma šípkami. Po spustení hry sa vieme s postavou hráča hry pohybovať na podlahe. Označením možnosti Freeze Rotation Z v okienku Inspector zabezpečí, aby sa naša postava hráča pri skoku alebo kontakte s iným telesom nepretáčal.

Vytváranie prekážok

Vytvoríme nový objekt: prekážku. Aj pri tomto objekte nastavíme collider. Ak je potrebné, collider prispôsobíme tvaru objektu. Collider nastavený pre hlavnú postavu a pre prekážku nám umožňuje, aby hlavný hrdina postupoval v hre ďalej iba prekonaním prekážky. Tento objekt odporúčame uložiť ako prefabrikovaný prvok, keďže počas hry budeme potrebovať mnoho ďalších prekážok. Vytvoríme nový Prefab, na ktorý aplikujeme náš objekt (prekážku). Celý proces je viditeľný na videoukážke. Získame tak objekt so špecifickými parametrami a nastaveniami, ktorý môžeme opakovane použiť kedykoľvek v danej scéne.

Vytváranie autobusu

Pre vytvorenie objektu autobus nepotrebujeme komponent Rigidbody 2D. Jednoducho si určíme podmienky v skripte BusController, ktoré objektu autobusu povoľujú vodorovný pohyb smerom doprava. Objekt doplníme značkou Finish.

void Update() {
    if (PlayerController.isBusTouched)
    {
        transform.Translate(0f, 0f, 0f);
    }
    else  transform.Translate(Time.deltaTime*speed, 0f, 0f);
}

Zvyšovanie rýchlosti po dotknutí sa objektu Coffee

Vytvoríme objekt s názvom Coffee s nastavením collideru v 2D. Vytvoríme a následne naň používame vlastnú značku. Aj tento objekt uložíme ako prvok prefab, keďže sa mnohokrát bude vyskytovať počas hry. Ak sa hlavná postava dotkne objektu Coffee, ktorý vďaka značky zmizne, jej rýchlosť sa zdvojnásobí a následne plynule stráca svoje zrýchlenie. Zmenu rýchlosti postavy hráča zabezpečuje funkcia typu coroutine v skripte PlayerController.

void OnTriggerEnter2D(Collider2D obj)
{
    //ak hráč narazí do kávy
    if (obj.gameObject.CompareTag("Coffee"))
    {
        Debug.Log("The Coffee is touched");
        Destroy(obj.gameObject);
        StartCoroutine(DoubleTheSpeed());
    }
}

//zvýšenie rýchlosti
IEnumerator DoubleTheSpeed()
{
    runSpeed = 2f * runSpeed;
    //hráč sa dotkol druhej kávy v poradí 
    if (runSpeed > 10)
    {
        while (runSpeed > 5f)
        {
            runSpeed -= 0.5f;
            yield return new WaitForSeconds(0.25f);
        }
    }
    else
    {
        while (runSpeed > 5f)
        {
            runSpeed -= 0.5f;
            yield return new WaitForSeconds(0.5f);
        }
    }
    if (runSpeed != 5) runSpeed = 5;
}

Jednoduchá animácia ako spätná väzba

Keď sa postava hráča vďaka káve na chvíľu zrýchli, nie je isté, či je krátkodobé zrýchlenie postavy hráča zreteľne očividné. Vytvoríme objekt s názvom Text a umiestnime do kamerového náhľadu. Pod panelom Window otvoríme okienko Animation a tu označíme Text objekt. Vytvoríme a nazveme novú animáciu.

Otvorené okienko Animation
Otvorené okienko Animation

Nahrávanie spustíme stlačením červeného tlačidla v okienku Animation. Kurzor nastavíme na 0:30 a medzitým pomocou ​​vlastnosti Scale zväčšíme veľkosť textov o jeden a pol. V ďalšom kroku kurzor nastavíme na 1:00 a pomocou ​​vlastnosti Scale veľkosť textu nastavíme na pôvodnú veľkosť. Celý proces je viditeľný na videoukážke. Takto sme vytvorili jednu veľmi jednoduchú animáciu, ktorá umožňuje pulzovanie nápisu.

Vytvorenie jednoduchej animácie
Vytvorenie jednoduchej animácie

Aby sa tento nápis objavil len v prípade ak sa postava hráča dotkala objektu kávy, k tomu musíme v skripte PlayerController zabezpečujúcom pohyb postavy hráča definovať herný objekt s nasledovným kódom:

public GameObject SpeedText;
public GameObject ExtraSpeedText;

private void Start()
{
    runSpeed = 5f;
    jumpSpeed = 6.5f;

    isBusTouched = false;

    SpeedText.gameObject.SetActive(false);
    ExtraSpeedText.gameObject.SetActive(false);
}
...
void OnTriggerEnter2D(Collider2D obj)
{
    //ak hráč narazí do kávy
    if (obj.gameObject.CompareTag("Coffee"))
    {
        SpeedText.gameObject.SetActive(true);
        Debug.Log("The Coffee is touched");
        Destroy(obj.gameObject);
        StartCoroutine(DoubleTheSpeed());
    }
    ...
}

IEnumerator DoubleTheSpeed()
{
    runSpeed = 2f * runSpeed;
    //hráč sa dotkol druhej kávy v poradí 
    if (runSpeed > 10)
    {
        SpeedText.gameObject.SetActive(false);
        ExtraSpeedText.gameObject.SetActive(true);
        while (runSpeed > 5f)
        {
            runSpeed -= 0.5f;
            yield return new WaitForSeconds(0.25f);

        }
        ExtraSpeedText.gameObject.SetActive(false);
    }
    else
    {
        while (runSpeed > 5f)
        {
            runSpeed -= 0.5f;
            yield return new WaitForSeconds(0.5f);
        }
    }
    SpeedText.gameObject.SetActive(false);

    if (runSpeed != 5) runSpeed = 5;
}

Tento herný objekt je predvolene inaktívny, teda neviditeľný. Aktivuje sa len v prípade, ak sa zrýchlenie udialo, znova sa inaktivuje keď postava hráča naberie svoju pôvodnú rýchlosť. Spätná väzba funguje, ak sa v okienku Inspector pod skriptom PlayerController aplikovanom na postavu hráča objaví jeden nový štvorček pre každý definovaný herný objekt, do ktorého natiahneme objekt textu.

Nové štvorčeky pre nové herné objekty
Nové štvorčeky pre nové herné objekty

Sledovanie postavy hráča pomocou kamery

Ak sa hlavnou postavou pohybujeme, ľahko vykročíme z herného sveta. Aby sme tomu zabránili, vieme nastaviť kameru, aby nasledovala pohyb objektu, teda v našom prípade cestu postavy hráča.

Existujú dve riešenia:

  • Vytvoríme si vlastný kód.
  • Nainštalujeme Cinemachine — balík vytvorený pre tento cieľ.

Ak sa rozhodneme pre druhý spôsob v hornom menu sa nám objaví nové menu s názvom CineMachine. Po označení možnosti Create2D Camera dostaneme novú kameru. Vieme si nastaviť objekt, ktorý chceme nasledovať a vykonať ďalšie nastavenia.

Inštalácia balíka Cinemachine
Inštalácia balíka Cinemachine

Alebo použijeme vlastný kód v skripte CameraFollow aplikovaný na MainCamera, ktorý môže vyzerať takto:

[SerializeField]
private Transform targetFollow;

void Update()
{
    transform.position = new Vector3(Mathf.Clamp(targetFollow.position.x, 0f, 1000f),
                                     transform.position.y,
                                     transform.position.z);
}

Dosiahnutie cieľa, koniec hry

Vytvoríme ďalší nový objekt, ktorý bude obsahovať cieľovú vlajku, so značkou GameOver. Ak postava hráča zastihne autobus ešte pred cieľovou vlajkou, hra sa končí úspešne. Pokiaľ ale dorazí do cieľa skôr, než by zastihol autobus, misia sa považuje za neúspešnú. Pri nastaveniach colliderov autobusu a cieľovej vlajky zaškrtneme štvorček s názvom Is Trigger.

Inspector okienko vlajky
Inspector okienko vlajky

Ak sa postava hráča dotkne ktoréhokoľvek z nich tak sa spustia funkcie zadefinované kódom v skripte PlayerController:

void OnTriggerEnter2D(Collider2D obj)
{
    ...
    //ak hráč narazí do autobusu
    if (obj.gameObject.CompareTag("Finish"))
    {
        Debug.Log("The bus is touched");
        isBusTouched = true;
        ...
    }
    //ak hráč narazí do cieľovej vlajky
    if (obj.gameObject.CompareTag("GameOver"))
    {
        Destroy(GameObject.FindWithTag("Finish"));
        if (!isBusTouched)
        {
               ...
        }
    }
}

Koniec hry ​​oznámi vyskakujúci panel, v ktorom sa nachádzajú dve tlačidlá: jedno znova spustí hru, druhé aplikáciu ukončí. Tieto vytvárame podľa vyššie spomenutých postupov. Posledným krokom je vytvorenie herného sveta. Ľubovoľne rozmiestnime prekážky a kávy.

Rozmiestnenie prekážok
Rozmiestnenie prekážok

Následne hru skompilujeme. Zvolíme platformu, nastavíme ikonu hry, predvolený rozmer atď.

Okienko Build Settings
Okienko Build Settings

Po skompilovaní môžeme hru spustiť. Výsledok môžete vidieť vo videoukážke. Úplné zdrojové kódy hry nájdete v projekte na GitLabe

Záver

Unity 3D je užitočný nástroj na vytváranie hier. Pomocou ​​grafického vývojového prostredia si vedia základy vytvárania hier jednoducho osvojiť aj ​​začiatočníci, keďže vyžaduje minimum programovania a množstvo funkcionalít je možné nastaviť priamo pomocou tohto rozhrania. Je užitočný aj z hľadiska zoznámenia sa s objektovo orientovanými programovacími jazykmi. V článku sme predstavili, ako jednoducho sa v ňom pripravuje hra. Samozrejme mnoho funkcií ostalo neodhalených. Odporúčame Vám sa s týmto nástrojom zoznámiť, keďže sa v ňom skrýva mnoho možností a používa ho čoraz širší okruh firiem.