Lekce 6 - Aréna s bojovníky v Dartu
V minulé lekci, Bojovník do arény v Dartu, jsme si vytvořili třídu bojovníka. Hrací kostku máme hotovou z prvních lekcí objektově orientovaného programování. Dnes tedy dáme vše dohromady a vytvoříme funkční arénu. Dart tutoriál bude spíše oddechový a pomůže nám zopakovat si práci s objekty.
Potřebujeme napsat nějaký kód pro obsluhu bojovníků a výpis zpráv
uživateli. Samozřejmě ho nebudeme bušit rovnou do main.dart
,
ale vytvoříme si objekt Arena
, kde se bude zápas odehrávat.
main.dart
potom jen založí objekty a o zbytek se bude starat
objekt Arena
. Přidejme k projektu tedy poslední třídu a to
arena.dart
.
Třída bude víceméně jednoduchá, jako vlastnosti bude obsahovat 3 potřebné instance: 2 bojovníky a hrací kostku. V konstruktoru se tyto atributy naplní z parametrů. Kód třídy bude tedy následující (komentáře si dopiště):
class Arena { Bojovnik _bojovnik1; Bojovnik _bojovnik2; Kostka _kostka; Arena(this._bojovnik1, this._bojovnik2, this._kostka); }
Zamysleme se nad metodami. Z veřejných metod bude určitě potřeba jen ta
k simulaci zápasu. Výstup programu na konzoli uděláme trochu na úrovni a
také umožníme třídě Arena
, aby přímo ke konzoli
přistupovala. Rozhodli jsme se, že výpis bude v kompetenci třídy, jelikož
se nám to zde vyplatí. Naopak kdyby výpis prováděli i bojovníci, bylo by
to na škodu (nebyli by univerzální). Potřebujeme tedy metodu, která
vykreslí obrazovku s aktuálními údaji o kole a životy bojovníků. Zprávy
o útoku a obraně budeme chtít vypisovat s dramatickou pauzou, aby byl
výsledný efekt lepší, uděláme si pro takový typ zprávy ještě pomocnou
metodu. Začněme s vykreslením informační obrazovky:
void _vykresli() { print('-------------- Aréna --------------\n'); print('Zdraví bojovníků:\n'); print('$_bojovnik1 ${_bojovnik1.grafickyZivot()}'); print('$_bojovnik2 ${_bojovnik2.grafickyZivot()}'); }
Zde asi není co řešit, můžete si ještě obrazovku vyzdobit barevně, když budete chtít. Metoda je privátní, budeme ji používat jen uvnitř třídy.
Další privátní metodou bude výpis zprávy s dramatickou pauzou. Pro
pauzu využijeme funkci sleep()
z knihovny
dart:io
.
void _vypisZpravu(String zprava) { sleep(new Duration(milliseconds: 500)); print(zprava); }
Třída Duration
nám umožní hezky nastavit čas trvání.
Můžete si všimnout, že parametr třídy Duration
přidáváme s
popiskem před hodnotou. Již víme, že v Dartu existuje speciální typ
parametrů - nepovinné parametry. I nepovinné parametry mají ještě
speciální podtyp - pojmenované (nepovinné) parametry. To
jsou nepovinné parametry, které se funkci musí zadat s jejich popiskem.
Používat pojmenované parametry se hodí zejména tehdy, pokud má metoda
mnoho parametrů a/nebo by nebylo zřejmé, co který parametr přesně
znamená. Tyto parametry umisťujeme do složených závorek a taktéž se u
nich může nastavovat výchozí hodnota. Tyto parametry přidáváme až po
"obyčejných" nepovinných parametrech.
Jak přesně tyto pojmenované nepovinné parametry fungují pochopíte z následující ukázky:
void mojeFunkce(int normalniParametr, [bool nepovinnyParametr = false], {String nepovinnyPojmenovanyParametr = 'wau'}) {} void main() { mojeFunkce(5, true); mojeFunkce(5, false, nepovinnyPojmenovanyParametr: 'hahaha'); mojeFunkce(5, nepovinnyPojmenovanyParametr: 'To je prostě dost super!'); }
Zpět k naší aréně. Obě metody vlastně jen vypisují na konzoli,
připadá mi zbytečné je zkoušet, přesuneme se tedy již k samotnému
zápasu. Metoda zapas()
nebude mít žádné parametry a nebude ani
nic vracet. Uvnitř bude cyklus, který bude na střídačku volat útoky
bojovníků navzájem a vypisovat informační obrazovku a zprávy.
Metoda by mohla vypadat takto:
void zapas() { print('Vítejte v aréně!'); print('Dnes se utkají $_bojovnik1 s $_bojovnik2! \n'); print('Zápas může začít...'); // cyklus s bojem while (_bojovnik1.nazivu() && _bojovnik2.nazivu()) { _bojovnik1.utoc(_bojovnik2); _vykresli(); _vypisZpravu(_bojovnik1.vratPosledniZpravu()); // zpráva o útoku _vypisZpravu(_bojovnik2.vratPosledniZpravu()); // zpráva o obraně _bojovnik2.utoc(_bojovnik1); _vykresli(); _vypisZpravu(_bojovnik2.vratPosledniZpravu()); // zpráva o útoku _vypisZpravu(_bojovnik1.vratPosledniZpravu()); // zpráva o obraně print(''); } }
Kód vypíše jednoduché informace a následně přejde do cyklu s bojem.
Jedná se o while
cyklus, který se opakuje, dokud jsou oba
bojovníci naživu. První bojovník zaútočí na druhého, jeho útok
vnitřně zavolá na druhém bojovníkovi obranu. Po útoku vykreslíme
obrazovku s informacemi a dále zprávy o útoku a obraně pomocí naší metody
_vypisZpravu()
, která po výpisu udělá dramatickou pauzu. To
samé provedeme i pro druhého bojovníka.
Přesuňme se do main.dart
, vytvořme patřičné instance a
zavolejme na aréně metodu zapas()
:
// vytvoření objektů Kostka kostka = new Kostka(10); Bojovnik zalgoren = new Bojovnik('Zalgoren', 100, 20, 10, kostka); Bojovnik shadow = new Bojovnik('Shadow', 60, 18, 15, kostka); Arena arena = new Arena(zalgoren, shadow, kostka); // zápas arena.zapas();
Charakteristiky hrdinů si můžete upravit dle libosti. Program spustíme:
Konzolová aplikace
-------------- Aréna --------------
Zdraví bojovníků:
Zalgoren [###### ]
Shadow [ ]
Shadow útočí úderem za 20 hp
Zalgoren utrpěl poškození 4 hp
Výsledek je docela působivý. Objekty spolu komunikují, grafický život ubývá jak má, zážitek umocňuje dramatická pauza. Aréna má však 2 nedostatky.
- V cyklu s bojem útočí první bojovník na druhého. Poté však vždy
útočí i druhý bojovník, nehledě na to, zda ho první nezabil. Může tedy
útočit již jako mrtvý. Podívejte se na screenshot výše, Shadow útočil
jako poslední i když byl mrtvý. Až potom se vystoupilo z
while
cyklu. U prvního bojovníka tento problém není, u druhého musíme před útokem kontrolovat, zda je naživu. - Druhým nedostatkem je, že bojovníci vždy bojují ve stejném pořadí,
čili zde "Zalgoren" má vždy výhodu. Pojďme vnést další prvek náhody a
pomocí kostky rozhodněme, který z bojovníků bude začínat. Jelikož jsou
bojovníci vždy dva, stačí hodit kostkou a podívat se, zda padlo číslo
menší nebo rovné polovině počtu stěn kostky. Tedy např. pokud padne na
desetistěnné kostce číslo do 5ti, začíná 2. bojovník, jinak začíná
první. Zbývá zamyslet se nad tím, jak do kódu zanést prohazování
bojovníků. Jistě by bylo velmi nepřehledné opodmínkovat příkazy ve
while
cyklu. Jelikož již víme, že v Dartu fungují reference, není pro nás problém udělat si 2 proměnné, ve kterých budou instance bojovníků, nazvěme je jednodušeb1
ab2
. Do těchto proměnných si na začátku dosadíme bojovníkybojovnik1
abojovnik2
tak, jak potřebujeme. Můžeme tedy při pozitivním hodu kostkou dosadit dob1
bojovník2
a naopak, výsledkem bude, že začínat bude ten druhý. Kód cyklu se takto vůbec nezmění a zůstane stále přehledný a jednoduchý, jen místobojovnik
budeb
.
Změněná verze včetně podmínky, aby nemohl útočit mrtvý bojovník, by mohla vypadat nějak takto:
void zapas() { // původní pořadí Bojovnik b1 = _bojovnik1; Bojovnik b2 = _bojovnik2; print('Vítejte v aréně!'); print('Dnes se utkají $_bojovnik1 s $_bojovnik2! \n'); // prohození bojovníků bool zacinaBojovnik2 = _kostka.hod() <= (_kostka.vratPocetSten() / 2); if (zacinaBojovnik2) { b1 = _bojovnik2; b2 = _bojovnik1; } print('Začínat bude bojovník $b1!\nZápas může začít...'); // cyklus s bojem while (b1.nazivu() && b2.nazivu()) { b1.utoc(b2); _vykresli(); _vypisZpravu(b1.vratPosledniZpravu()); // zpráva o útoku _vypisZpravu(b2.vratPosledniZpravu()); // zpráva o obraně if (b2.nazivu()) { b2.utoc(b1); _vykresli(); _vypisZpravu(b2.vratPosledniZpravu()); // zpráva o útoku _vypisZpravu(b1.vratPosledniZpravu()); // zpráva o obraně } print(''); } }
Program vyzkoušejme:
Konzolová aplikace
-------------- Aréna --------------
Zdraví bojovníků:
Zalgoren [######### ]
Shadow [ ]
Zalgoren útočí úderem za 27 hp
Shadow utrpěl poškození 11 hp a zemřel
Vidíme, že je vše již v pořádku. Gratuluji vám, pokud jste se dostali až sem a tutoriály opravdu četli a pochopili. Máte základy objektového programování a dokážete tvořit rozumné aplikace.
V příští lekci, Dědičnost a polymorfismus v Dartu, se podíváme na objektově orientované programování podrobněji. V úvodu jsme si říkali, že OOP stojí na pilířích: zapouzdření, dědičnost a polymorfismus. První umíme již velmi dobře a modifikátor private (podtržítko) je nám známý. Další dva nás čekají příště.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 6x (3.88 kB)
Aplikace je včetně zdrojových kódů v jazyce Dart