08. Používateľské rozhranie

Len málo interaktívnych aplikácií sa zaobíde bez nejakého grafického používateľského rozhrania (UI). Do našej hry dorobíme najprv grafickú informáciu o skóre a nabudúce aj okno, ktoré sa zobrazí pri pauznutí hry.

UI prvky sú napríklad tlačidlá, okienka, textové výpisy, ikony. Unity ich umiestňuje v 3D scéne, aj keď majú 2D povahu. Vyskúšajme hneď:

Vytvorte v scéne nový game objekt typu UI > Text a pomenujte ho Pocitadlo

Všimnime si dve veci:

V hierachii sa okrem Pocitadla vytvoril aj game objekt Canvas a Pocitadlo je zaradene pod neho. Prvky UI sú v Unity vždy zaradené pod špeciálny game objekt typu canvas, ktorý reprezentuje 2D obrazovku. Je to preto, že UI prvky nebudeme rozmiestňovať v 3D scéne ale v 2D obdĺžniku tohto canvasu.

Canvas je u nás v scéne mnohonásobne väčší ako stôl, ale to je preto, že stôl má veľkosť okolo 20 a canvas je veľký ako obrazovka v pixeloch, takže okolo 1000. Vôbec to nevadí, lebo vo výslednej hre sa canvas nezobrazuje ako objekt v 3D scéne ale ako 2D vrstva na obrazovke „pred“ 3D scénou.

Súradnice v canvase

V canvase sa pozícia objektov nastavuje podľa inej filozofie ako v 3D scéne. Vďaka tomu môžeme objekty zarovnávať na rôzne strany a vedia sa automaticky prispôsobiť rôznym rozmerom obrazoviek.

Všimnite si, že game objekt Pocitadlo nemá komponent Transform ale Rect Transform. (Rect = rectangle = obdĺžnik)

Teraz umiestnime počítadlo do ľavého dolného rohu obrazovky. Najprv nastavte počítadlu v komponente Rect Transform ukotvenie do ľavého dolného rohu canvasu:

Keď teraz počítadlu zmeníme hodnoty Pos X a Pos Y na 50, 50, ocitne sa v ľavom dolnom rohu canvasu:

Hodnoty Pos X a Pos Y určujú ako ďaleko je element od toho rohu, do ktorého je ukotvený. My sme ho posunuli o 50 pixelov doprava a 50 pixelov nahor od bodu kam má byť ukotvený.

Prečo potom nápis trčí von z canvasu? Je to preto, že sa v canvase elementy umiestňujú svojím stredom. A stred obdĺžnika Pocitadlo naozaj aj je 50 pixelov doprava a 50 pixelov nahor od ľavého dolného okraja. Budeme teda potrebovať nastaviť nielen kam je Pocitadlo ukotvene ale aj čím je tam ukotvené, ktorým svojím bodom.

To je vlastnosť Pivot. Nastavíme ju na X:0,Y:0. Každý objekt v canvase je nejaký obdĺžnik. Pivot mu definuje ktorý bod z tohto obdĺžnika je ten hlavný, okolo ktorého sa otáča, škáluje a ktorým sa ukotvuje. Štandardne je to stred (X:0.5, Y:0.5). Ľavý dolný roh je (0, 0), pravý horný (1, 1).

Po zmene pivota na (0,0) musíme ešte raz nastaviť Pos X a Pos Y na (50, 50). Unity naše pôvodné hodnoty (50, 50) zmenilo keď sme zmenili pivot, aby sa výsledná pozícia nezmenila. Áno, nie je to intuitívne, teraz to nemusí dávať zmysel, ale má to svoju logiku a pri dlhšej práci s UI v Unity to oceníte.

Po týchto zmenách už je náš textový element zarovnaný na ľavý dolný roh a odsadený od neho o 50 pixelov

  • Vytvorte ďalší textový UI element, pomenujte ho Cas
  • Ukotvite ho jeho ľavým horným okrajom do ľavého horného okraja obrazovky, tiež 50 pixelov od rohu. Aké hodnoty bude mať Pivot? Aké hodnoty Pos X a Pos Y?
  • Upravte farbu a veľkosť písma v komponente Text na game objektoch Pocitadlo a Cas.

Napojenie zo skriptu na UI

Ako asi tušíte, nechceme mať na obrazovke nápisy „New Text“. Budeme tam chcieť vypisovať počet úderov a uplynutý čas. To, čo sa v textovom elemente vypisuje je uložené vo vlastnosti Text na komponente Text. To je vlastnosť, ktorú potrebujeme meniť.

Informáciu o tom, koľko úderov hráč zahral, zaznamenáva skript Main na game objekte Hra. Akurát zatiaľ to vypisuje len do konzoly. Namiesto výpisu hodnoty do konzoly by sme chceli, aby nastavil text na obrazovke na túto hodnotu.

Aby sme zo skriptu Main vedeli meniť text na game objekte Pocitadlo, musíme sa na tento game objekt napojiť.

Vytvorte v skripte Main vlastnosť pocitadlo a uložte do nej game objekt Pocitadlo.

Predtým, ako začneme v skripte manipulovať s UI prvkami, budeme ešte potrebovať do skriptu doplniť zmienku o tom, že chceme používať UI prvky. Doplňte v skripte Main v časti úplne hore riadok using UnityEngine.UI;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
    public int shots = 0;
    public float time = 0;

Teraz môžeme v skripte Main namiesto vypisovania počtu úderov do konzoly vypisovať počet úderov priamo na obrazovku. Do game objektu, ktorý máme uložený vo vlastnosti pocitadlo, konkrétne do jeho komponentu Text a ešte konkrétnejšie do vlastnosti text v tomto komponente:

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            shots++;
            Debug.Log("Pocet uderov: " + shots);
            pocitadlo.GetComponent<Text>().text = shots;
        }

Pozor. Toto nám ešte nebude fungovať. Vlastnosť shots je typu int. Je to číslo. Ale vlastnosť text, do ktorej chceme priradiť hodnotu shots je textová vlastnosť. Je typu string. Môžeme ale urobiť jednoduchú konverziu z čísla na text, takto:

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            shots++;
            Debug.Log("Pocet uderov: " + shots);
            pocitadlo.GetComponent<Text>().text = shots.ToString();
        }

Po zahratí úderu sa v počítadle na obrazovke začne ukazovať aktuálny počet úderov:

A asi tušíte, čo bude vaša ďalšia úloha.

Upravte skript Main aby sa v texte v game objekte Cas zobrazovala hodnota uplynutého času. Túto hodnotu si počítame vo vlastnosti time. Ak rozmýšľate kam v kóde doplniť príkaz na nastavenie textu na hodnotu time, tak najlepšie hneď za to, ako hodnotu time zmeníme.

Na obrazovke sa už vypisuje aj bežiaci čas aj počet úderov. Akurát neznalý človek nevie úplne, čo tieto čísla znamenajú. Hodilo by sa ak nim pridať nejaké ikony.

Obrázky v UI

  • Vytvorte v assetoch adresár Icons
  • Vytvorte nový asset typu UI > Image
  • Cez komponent Rect Transform v inšpektore mu nastavte width a height na 32 a 32
  • Pomocou jeho pivota a ukotvenia ho zarovnajte do ľavého dolného rohu, naľavo od Pocitadla

Takýto novo vytvorený image sa zobrazuje ako biely štvorček, lebo v ňom zatiaľ nie je žiadny obrázok. Je prázdny. O tom, čo sa v obrázku zobrazí rozhoduje vlastnosť Source Image v jeho komponente Image. V inšpektore vidno, že zatiaľ má túto vlastnosť prázdnu : None (Sprite).

Tak mu tam skúsme priradiť obrázkový asset hit, ktorý sme si pred chvíľou stiahli.

A to nejde.

Textúra vs. sprite

Obrázkové assety v Unity môžu mať rôzne využitie. Už sme napríklad použili obrázok dreva ako textúru do materiálu pre obrubu stola. Teraz chceme použiť obrázok ako ikonku do UI. Z dôvodu optimalizácie výkonu si rôzne typy využitia vyžadujú rôzne typy obrázkových assetov: textúry, ikony, kurzory a pod. Ale Unity nemá samo od seba ako vedieť na aký účel ktorý obrázok chceme.

Defaultne sa obrázky naimportujú ako textúra pre použitie v materiáloch. Ak chceme obrázok použiť v UI, musíme Unity povedať, aby tento obrázkový súbor naimportovalo nanovo a tentoraz ako tzv. Sprite.

Označme v assetoch hit a v inšpektore mu zmeníme Texture Type na Sprite (2D and UI). Zmeny v importe assetov treba vždy ešte potvrdiť kliknutím na Apply:

Teraz hit už pôjde priradiť do Image source na našej novej ikonke:

Vytvorte pomocou assetu clock takto aj ikonu pre časomieru v ľavom hornom rohu obrazovky.

Možností pre vytváranie a nastavovanie elementov v UI je omnoho viac a neskôr sa k niektorým ďalším dostaneme aj tu. Zatiaľ si prečítajte manuál k UI systému v Unity.

Kozmetická úprava na záver

Na začiatku hry, ešte pred prvým úderom, ukazuje Pocitadlo nápis „New text“. Mala by tam byť nula, tak nastavime Pocitadlu cez inšpektor jeho počiatočný text z „New text“ na 0. A to isté môžeme urobiť pre game objekt Cas.

Hotový projekt po tejto lekcii: MojaHra_08.zip