04. Od premenných k atribútom

Na konci minulej lekcie bola úloha vyrobiť jednoduchý systém na pripočítavanie a odpočítavanie k zdravia. Vytvorili sme premenné zdravie a lekarnicka a vyskúšali sme odpočítať od zdravia nejakú hodnotu a následne pripočítať k zdraviu hodnotu lekárničky. Takto nejak by mohol vyzerať výsledný skript:

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

public class Health : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        float zdravie = 100f;
        float lekarnicka = 10f;

        Debug.Log("Zdravie je " + zdravie);

        zdravie -= 30;
        Debug.Log("Zdravie je " + zdravie);

        zdravie += lekarnicka;
        Debug.Log("Zdravie je " + zdravie);

        zdravie *= 1.5f;
        Debug.Log("Zdravie je " + zdravie);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Prípadne sa pripočítavanie/odpočítavanie zdravia dá napísať aj ekvivalentným spôsobom: zdravie = zdravie - 30 a zdravie = zdravie + lekarnicka a zdravie = 1.5f * zdravie.

Toto bola pochopiteľne len úloha na prácu s premennými, v skutočnosti by sa vo videohre zdravie neodpočítavalo a nepripočítavalo hneď na začiatku (v metóde Start) ale dialo by sa to počas behu hry, na základe nejakých udalostí, ktoré nastanú – napr. zásah od nepriateľa alebo zobratie lekárničky. A to teraz aj urobíme.

Viditeľnosť premennej, kontext

My teraz doplníme do skriptu Health jednoduchú simuláciu takýchto udalostí. Na stlačenie ľavého tlačidla myši budeme simulovať zásah a odpočítame zdravie. Na stlačenie pravého tlačidla myši budeme simulovať lekárničku a pripočítame zdravie. Zatiaľ si len takto doplňte metódu Update:

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

public class Health : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        float zdravie = 100f;
        float lekarnicka = 10f;

        Debug.Log("Zdravie je " + zdravie);

        zdravie -= 30;
        Debug.Log("Zdravie je " + zdravie);

        zdravie += lekarnicka;
        Debug.Log("Zdravie je " + zdravie);

        zdravie *= 1.5f;
        Debug.Log("Zdravie je " + zdravie);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            zdravie -= 30;
            Debug.Log("Zdravie je " + zdravie);
        }
    }
}

Preložené do ľudskej reči, čo sme napísali do Update:

  • if : Ak
  • Input.GetMouseButtonDown : bolo stlačené
  • (0): ľavé tlačidlo myši
  • zdravie -= 30 : zníž zdravie o 30

Zatiaľ tomu nemusíme veľmi rozumieť, bližšie si toto vysvetlíme nabudúce. Teraz je podstatné, že chceme v metóde Update manipulovať s premennou zdravie.

Avšak, už samotný editor nás upozorňuje, že niekde je chyba (červené podčiarknutie) a to isté aj Unity v konzole (červená chybová hláška):

Problém je tu v tom, že my sme premennú zdravie zadefinovali vnútri v metóde Start a tam ona funguje. Ale premenná zadefinovaná v jednej metóde nie je viditeľná pre iné metódy. Tomu sa hovorí aj kontext. V kontexte metódy Start existuje premenná zdravie ale neexistuje už v kontexte metódy Update.

Čo keby ste tento problém vyriešili tak, že zadefinujeme premennú zdravie aj v metóde Update. Takto napríklad:

    void Update()
    {
        float zdravie = 100f;

        if (Input.GetMouseButtonDown(0))
        {
            zdravie -= 30;
            Debug.Log("Zdravie je " + zdravie);
        }
    }

Chybové hlášky zmizli, hra už ide spustiť. Tak vyskúšajme, čo to robí. Spustime hru. Konzola vypíše tie isté 4 výpisy, čo predtým. Potiaľto nič nové, toto bola metóda Start, v ktorej sme nič nemenili.

Teraz kliknite myšou do bežiacej hry, konzola vypíše:

Je to v poriadku, má tam byť hodnota 70?

Tento piaty výpis už vznikol dianím v metóde Update. To je ten kód ktorý sme tam teraz pridávali. Ale prečo je tam hodnota 70, keď po štvrtom výpise bola v premennej zdravie hodnota 120 a kliknutím sme ju znížili o 30. Nemalo by tam byť 90?

Malo! A prečo tam teda nie je?

Je to preto, že síce v metóde Update znižujeme premennú zdravie, ale znižujeme úplne inú premennú s menom zdravie. Konkrétne premennú s menom zdravie v metóde Update. Je to len menovkyňa prvej premennej, nie je to tá istá premenná.

Každá metóda má totiž svoj vlastný kontext. A keď sme v kontexte metódy Update zadefinovali premennú zdravie, je to úplne nová premenná a len zhodou okolností má ten istý názov ako zdravie v kontexte metódy Start.

Je to ako keď na každom stole v reštaurácii je taká istá krabička s obrúskami. Taká istá, ale nie tá istá. Sú to nezávislé krabičky a v každej je iné množstvo obrúskov. My tu máme na stole Start krabičku, v ktorej je 120 a na stole Update je krabička, v ktorej je 70.

Od premennej k vlastnosti

Ak chceme, aby bola premenná spoločná pre rôzne metódy, musíme si ju zadefinovať v kontexte, ktorý je pre tieto metódy spoločný. V našom prípade je to kontext celého skriptu Health.

Zrušíme zo stolov samostatné krabičky a dáme jednu krabičku pre celú reštauráciu.

V kóde to znamená, že odstránime lokálne definície premennej zdravie z metód Start aj Update. A zadefinujeme novú premennú zdravie na úrovni celého skriptu.

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

public class Health : MonoBehaviour
{
    float zdravie = 100f;

    // Start is called before the first frame update
    void Start()
    {
        float lekarnicka = 10f;

        Debug.Log("Zdravie je " + zdravie);

        zdravie -= 30;
        Debug.Log("Zdravie je " + zdravie);

        zdravie += lekarnicka;
        Debug.Log("Zdravie je " + zdravie);

        zdravie *= 1.5f;
        Debug.Log("Zdravie je " + zdravie);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            zdravie -= 30;
            Debug.Log("Zdravie je " + zdravie);
        }
    }
}

Uložme skript a spustime hru. Čo vypíše teraz po kliknutí?

Po stave 120 sa kliknutím myšou znížil stav zdravia o 30 na hodnotu 90, presne tak, ako by to malo byť.

Premenná zadefinovaná na úrovni celého skriptu sa volá vlastnosť alebo aj atribút. Prísne programátorsky vzaté je vlastnosť niečo trochu iné ako atribút, ale nateraz nemusíme zachádzať do takej hardcore terminológie a vystačíme si so spoločným pojmom vlastnosť.

Premennú zdravie, ktorá doteraz existovala len lokálne, v kontexte metódy Start sme teda povýšili do hodnosti vlasnosť a existuje už v kontexte celého skriptu Health, a tým pádom s ňou môžu narábať všetky metódy v skripte Health.

Urobte vlastnosť aj z premennej lekarnicka, ktorá je momentálne zadefinovaná len v kontexte metódy Start.

A keď už máme aj lekárničku povýšenú na vlastnosť, vieme ju použiť v metóde Update a pridať k odčítavaniu zdravia pri stlačení jedného tlačítka myši aj pripočítavanie zdravia pri stlačení druhého tlačítka myši:

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            zdravie -= 30;
            Debug.Log("Zdravie je " + zdravie);
        }

        if (Input.GetMouseButtonDown(1))
        {
            zdravie += lekarnicka;
            Debug.Log("Zdravie je " + zdravie);
        }
    }

Uložiť, vyskúšať. Pri klikaní pravým a ľavým tlačidlo sa už zdravie odpočítava a pripočítava podľa hodnôt v skripte:

Ako nastavovať vlastnosti z inšpektora

Nastavovať hodnoty vlastnostiam v skripte nemusí byť vždy najpohodlnejšie a máme na to aj lepší spôsob. Vieme sprístupniť vlastnosť skriptu tak, aby ju bolo vidieť v inšpektore v Unity.

Náš skript zatiaľ nemá v inšpektore žiadne viditeľné vlastnosti:

Ale ak v skripte pridáme pred definície vlastností zdravie a lekarnicka kúzelné slovo public, stanú sa tieto vlastnosti verejnými a bude ich vidieť v inšpektore:

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

public class Health : MonoBehaviour
{
    public float zdravie = 100f;
    public float lekarnicka = 10f;

    // Start is called before the first frame update
    void Start()

Veľkou výhodou takto zverejnených vlastností je, že ich môžeme meniť v inšpektore nezávisle od skriptu.

Zmeňte v inšpektore hodnotu Lekarnicka na 20, spustite hru a sledujte, aké hodnoty vypisuje konzola.

Pri klikaní pravým tlačítkom sa už zdravie nezvyšuje o 10 ale o 20 bodov:

Hodnoty nastavené v inšpektore majú vyššiu prioritu ako hodnoty v skripte. Má to logiku, pretože ten istý skript môže byť na viacerých game objektoch a my chceme, aby mal každý game objekt vlastnú autonómiu v tom, koľko má zdravia napríklad. Bolo by nezmyselné, aby mali všetci rovnaké zdravie.

Nezabúdajme, že skript – v našom prípade Health – je len asset. Niečo ako textúra. Mohli by sme ho pridať na viaceré game objekty. A na každom game objekte by si potom už žil svojím vlastným životom. Hoci je skript Health len jeden, tak každý taký game objekt by mal svoj vlastný komponent Health, nezávislý na ostatných komponentoch.

Všimnite si tiež, že počas behu hry, ako sa mení hodnota zdravia, tak sa mení aj hodnota ukázaná v inšpektore:

Funguje to teda obidvoma smermi. Keď nastavíme vlastnosť v inšpektore, zmení sa jej hodnota. A keď sa zmení jej hodnota pri vykonávaní skriptu v komponente, aktualizuje sa podľa tohto aj jej hodnota v inšpektore.

  • Vyrobte v skripte Health vlastnosť zasah
  • Nahraďte čísla 30, tam kde sa odpočítava zdravie, vlastnosťou zasah.
  • Nastavte v inšpektore hodnotu vlastnosti Zasah na 10.

Ešte upraceme na záver

Teraz, keď už nám zásahy aj lekárničky fungujú na klikanie myšou, môžeme zrušiť ich testovanie, ktoré sme pôvodne robili v metóde Start. Zjednodušme si metódu Start tak, aby len na začiatku vypísala do konzoly počiatočné zdravie:

    void Start()
    {
        Debug.Log("Zdravie je " + zdravie);
    }

Hotový projekt po tejto lekcii: Programovanie_04.zip