06. Podmienky v praxi

Vráťme sa teraz k skriptu Health.cs, v ktorom na klikanie myšou pridávame alebo uberáme zdravie. Na konci minulej lekcie ste dostali za úlohu pridať do metódy Update podmienku, ktorá by vypísala Game Over v momente, keď zdravie klesne na nulu.

Dá sa to urobiť viacerými spôsobmi. Jeden je napríklad tento:

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

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

        if (zdravie <= 0)
        {
            Debug.Log("Game over");
        }
    }

Ak vyskúšame skript v tejto podobe, bude síce fungovať, ale má jednu nepríjemnú vlastnosť:

Od dosiahnutia hodnoty 0 už navždy a neustále vypisuje Game over. To je zbytočné a nežiadúce. Prečo sa to deje?

Metóda Update sa vykonáva neustále, v každom jednom frejme (snímku). A kým bude hodnota zdravia na nule, bude v každom jednom frejme Unity vykonávať príkaz na výpis Game over.

Vieme to zmeniť?

Game over potrebujeme vypísať len vtedy, keď zdravie klesne na nulu. Nie neustále. A kde sa nám deje, že zdravie klesá? Je to tu:

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

Toto je podmienka, v ktorej bloku sa znižuje zdravie. Tento blok sa nevykonáva v každom frejme. Vykonáva sa len vtedy, keď stlačíme ľavý myš. Ak teda presunieme našu podmienku o zdraví na nule sem, bude sa vykonávať len vtedy, keď stlačíme ľavý myš a nie neustále:

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

            if (zdravie <= 0)
            {
                Debug.Log("Game over");
            }
        }

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

Toto je nová vec. Máme tu podmienku if (Input.GetMouseButtonDown(0))a vnútri v jej tele, medzi zátvorkami { a } sa nachádza ešte jedna podmienka if (zdravie <= 0) so svojím vlastným telom.

Podmienky môžeme takto do seba vkladať ako malé krabičky do väčších krabíc. Bloky uhraničené zátvorkami {...} fungujú ako také krabičky. Ak si všimneme, tak skript Health má svoj veľký blok, v ňom sú vložené metódy Start a Update, každá so svojim blokom. V bloku metódy Update máme vložené podmienky, tiež každú so svojim blokom. A do prvej podmienky, ktorá sa týka ľavého tlačidla myši, sme teraz vložili ďalšiu podmienku, ktorá sa týka nulového zdravia.

Keď to už máme takto upratané, skúsme, či program funguje ako má:

Už je to lepšie, konzola už nevypisuje donekonečna Game over. Vypíše ho raz – keď sme dosiahli hodnotu 0.

A potom ho vypíše znova, ak sme klikli ešte raz a zdravie kleslo na -10.

To asi nechceme. Teda… asi nechceme, aby zdravie kleslo na -10. Nedáva to zmysel a budeme chcieť, aby zdravie ostalo na 0 a už sa ďalej neznižovalo.

Skúste upraviť program tak, aby znižoval zdravie len vtedy, keď je väčšie ako 0. Ak vám to naozaj ale že fakt že vôbec nejde, čítajte ďalej. Ale skúste to najprv sami.

Môžeme začať tým, že skúsime obmedziť príkaz na znižovanie zdravia len na prípady, kedy je zdravie väčšie ako nula:

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

            if (zdravie <= 0)
            {
                Debug.Log("Game over");
            }
        }

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

Príkaz na znižovanie zdravia sme zavreli do podmienky if (zdravie > 0). Toto už naozaj nebude znižovať zdravie pod nulu, ale pri každom ďalšom kliku sa nám stále ešte bude zbytočne vypisovať stav zdravia aj Game over:

Čo keby sme teda zavreli do tejto podmienky aj výpis zdravia aj podmienku na Game over?

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

                if (zdravie <= 0)
                {
                    Debug.Log("Game over");
                }
            }
        }

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

Takto napísaný program bude počítač čítať nasledovne:

  • Bol stlačený ľavý myš?
    • Je zdravie väčšie ako nula?
      • Zníž zdravie
      • Vypíš jeho hodnotu
      • Je zdravie na nule?
        • Vypíš Game over ak áno.

Toto už robí, čo chceme. Po dosiahnutí nuly sa už zdravie neznižuje, ani sa nič nevypisuje zbytočne:

  • Upravte pridávanie zdravia tak, aby zdravie nemohlo byť väčšie ako 100

Zložené logické výrazy

Predstavme si teraz, že chceme, aby sa zdravie zvyšovalo nie len pri stlačení pravého myšidla, ale aj pri stlačení klávesy L (ako lekarnicka).

Zatiaľ vieme, že Input.GetMouseButtonDown(1) nám zisťuje, či bol alebo nebol stlačený pravý myš.

Či bol stlačený kláves L zistíme podobne, pomocou príkazu Input.GetKeyDown(KeyCode.L). Ako by sme to teraz použili v kóde?

Jedna možnosť je napísať celú novú podmienku, ktorá len kopíruje príkazy z podmienky na stlačenie pravej myši:

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

        if (Input.GetKeyDown(KeyCode.L))
        {
            if (zdravie < 100)
            {
                zdravie += lekarnicka;
                Debug.Log("Zdravie je " + zdravie);
            }
        }

Je to funkčné a robí to, čo má. Môžete si to vyskúšať, ak chcete. Ale je to dosť nešťastné riešenie. Keby sme chceli upraviť program a pridať do neho napríklad príkaz, ktorý pri dosiahnutí hodnoty 100 prehrá nejaký zvuk, tak by sme ho museli pridávať na dvoch miestach programu – aj do bloku pod podmienku na stlačenie pravej myši aj pod podmienku na stlačenie klávesy L.

Toto je veľmi zlý spôsob programovania. V podstate vždy, keď sa v programe vyskytuje taký istý kód viackrát, snažíme sa to zjednotiť do jedného bloku, aby sme následne všetky úpravy robili iba na jednom mieste.

Ale ako zjednotíme dve podmienky do jednej?

Čo keby sme namiesto dvoch rôznych otázok …

  • Bol stlačený ľavý myš?
  • Bol stlačený kláves L?

… vedeli položiť iba jednu otázku:

  • Bol stlačený ľavý myš alebo bol stlačený kláves L?

V jazyku C# na takéto skladanie logických výrazov použijeme logický operátory alebo. Operátor alebo sa zapisuje ako || a v kóde to potom bude vyzerať takto:

    if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.L))
    {
        if (zdravie < 100)
        {
            zdravie += lekarnicka;
            Debug.Log("Zdravie je " + zdravie);
        }
    }

Znak | je na každej klávesnici niekde inde, budete mať možno problém ho rýchlo nájsť. Najťažšie sa hľadá na slovenskej klávesnici, nie je tam totiž vôbec a treba ho písať kombináciou AltGr+W. Ja odporúčam používať americké rozloženie kláves pri programovaní, lebo sa tam ľahko píšu aj zátvorky { }, [ ] aj značky < >. Ale je to na individuálnom vkuse každého.

Prispôsobte aj odpočítavanie zdravia, aby sa spúšťalo nie len pri stlačení ľavého myšidla, ale aj pri stlačení klávesy Z (ako zásah).

Okrem operátora „alebo“ ( || ) poznáme aj operátor „a zároveň“ ( && ) a potom ešte operátor „nie“ ( ! ) ale o nich si povieme, keď sa vyskytnú.

Pozrite si rekapituláciu aritmetických aj logických operátorov v C# na stránke W3C Schools

Hotový projekt po tejto lekcii: Programovanie_06.zip